优化同步逻辑
parent
4ad9c81381
commit
1c159f59bb
|
@ -23,6 +23,7 @@
|
||||||
"image-size": "^1.0.2",
|
"image-size": "^1.0.2",
|
||||||
"jschardet": "^3.0.0",
|
"jschardet": "^3.0.0",
|
||||||
"long": "^5.2.3",
|
"long": "^5.2.3",
|
||||||
|
"message2call": "^0.1.0",
|
||||||
"music-metadata": "^8.1.4",
|
"music-metadata": "^8.1.4",
|
||||||
"needle": "github:lyswhut/needle#93299ac841b7e9a9f82ca7279b88aaaeda404060",
|
"needle": "github:lyswhut/needle#93299ac841b7e9a9f82ca7279b88aaaeda404060",
|
||||||
"node-id3": "^0.2.6",
|
"node-id3": "^0.2.6",
|
||||||
|
@ -45,7 +46,7 @@
|
||||||
"@tsconfig/recommended": "^1.0.2",
|
"@tsconfig/recommended": "^1.0.2",
|
||||||
"@types/better-sqlite3": "^7.6.4",
|
"@types/better-sqlite3": "^7.6.4",
|
||||||
"@types/needle": "^3.2.0",
|
"@types/needle": "^3.2.0",
|
||||||
"@types/node": "^20.4.9",
|
"@types/node": "^20.4.10",
|
||||||
"@types/spinnies": "^0.5.0",
|
"@types/spinnies": "^0.5.0",
|
||||||
"@types/tunnel": "^0.0.3",
|
"@types/tunnel": "^0.0.3",
|
||||||
"@types/ws": "8.5.4",
|
"@types/ws": "8.5.4",
|
||||||
|
@ -3088,9 +3089,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.4.9",
|
"version": "20.4.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.10.tgz",
|
||||||
"integrity": "sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==",
|
"integrity": "sha512-vwzFiiy8Rn6E0MtA13/Cxxgpan/N6UeNYR9oUu6kuJWxu6zCk98trcDp8CBhbtaeuq9SykCmXkFr2lWLoPcvLg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/plist": {
|
"node_modules/@types/plist": {
|
||||||
|
@ -8749,6 +8750,11 @@
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/message2call": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/message2call/-/message2call-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-10zubfIMTkxevtenynScyYN6GdWUgROrGqi9rU06tg+WY8SJIPwvKaDFuoKF8d2pbaFXD4hlOON2vf0ptCIN/A=="
|
||||||
|
},
|
||||||
"node_modules/micromatch": {
|
"node_modules/micromatch": {
|
||||||
"version": "4.0.5",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||||
|
@ -15018,9 +15024,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "20.4.9",
|
"version": "20.4.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.10.tgz",
|
||||||
"integrity": "sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==",
|
"integrity": "sha512-vwzFiiy8Rn6E0MtA13/Cxxgpan/N6UeNYR9oUu6kuJWxu6zCk98trcDp8CBhbtaeuq9SykCmXkFr2lWLoPcvLg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/plist": {
|
"@types/plist": {
|
||||||
|
@ -19282,6 +19288,11 @@
|
||||||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"message2call": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/message2call/-/message2call-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-10zubfIMTkxevtenynScyYN6GdWUgROrGqi9rU06tg+WY8SJIPwvKaDFuoKF8d2pbaFXD4hlOON2vf0ptCIN/A=="
|
||||||
|
},
|
||||||
"micromatch": {
|
"micromatch": {
|
||||||
"version": "4.0.5",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "lx-music-desktop",
|
"name": "lx-music-desktop",
|
||||||
"version": "2.4.0-beta.6",
|
"version": "2.4.0-beta.7",
|
||||||
"description": "一个免费的音乐查找助手",
|
"description": "一个免费的音乐查找助手",
|
||||||
"main": "./dist/main/main.js",
|
"main": "./dist/main/main.js",
|
||||||
"productName": "lx-music-desktop",
|
"productName": "lx-music-desktop",
|
||||||
|
@ -212,7 +212,7 @@
|
||||||
"@tsconfig/recommended": "^1.0.2",
|
"@tsconfig/recommended": "^1.0.2",
|
||||||
"@types/better-sqlite3": "^7.6.4",
|
"@types/better-sqlite3": "^7.6.4",
|
||||||
"@types/needle": "^3.2.0",
|
"@types/needle": "^3.2.0",
|
||||||
"@types/node": "^20.4.9",
|
"@types/node": "^20.4.10",
|
||||||
"@types/spinnies": "^0.5.0",
|
"@types/spinnies": "^0.5.0",
|
||||||
"@types/tunnel": "^0.0.3",
|
"@types/tunnel": "^0.0.3",
|
||||||
"@types/ws": "8.5.4",
|
"@types/ws": "8.5.4",
|
||||||
|
@ -262,6 +262,7 @@
|
||||||
"image-size": "^1.0.2",
|
"image-size": "^1.0.2",
|
||||||
"jschardet": "^3.0.0",
|
"jschardet": "^3.0.0",
|
||||||
"long": "^5.2.3",
|
"long": "^5.2.3",
|
||||||
|
"message2call": "^0.1.0",
|
||||||
"music-metadata": "^8.1.4",
|
"music-metadata": "^8.1.4",
|
||||||
"needle": "github:lyswhut/needle#93299ac841b7e9a9f82ca7279b88aaaeda404060",
|
"needle": "github:lyswhut/needle#93299ac841b7e9a9f82ca7279b88aaaeda404060",
|
||||||
"node-id3": "^0.2.6",
|
"node-id3": "^0.2.6",
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
目前本项目的原始发布地址只有 **GitHub** 及 **蓝奏网盘** ,其他渠道均为第三方转载发布,可信度请自行鉴别。
|
目前本项目的原始发布地址只有 **GitHub** 及 **蓝奏网盘** ,其他渠道均为第三方转载发布,可信度请自行鉴别。
|
||||||
本项目无微信公众号之类的官方账号,谨防被骗。
|
本项目无微信公众号之类的官方账号,谨防被骗。
|
||||||
|
|
||||||
|
### 不兼容性变更
|
||||||
|
|
||||||
|
该版本修改了同步协议逻辑,需要PC端v2.4.0或移动端v1.7.0版本才能连接使用。
|
||||||
|
|
||||||
### 新增
|
### 新增
|
||||||
|
|
||||||
- 新增我的列表名右键菜单-排序歌曲-随机乱序功能,使用它可以对选中列表内歌曲进行随机重排(#1440)
|
- 新增我的列表名右键菜单-排序歌曲-随机乱序功能,使用它可以对选中列表内歌曲进行随机重排(#1440)
|
||||||
|
@ -10,6 +14,7 @@
|
||||||
- 优化音效设置-环境音效启用、禁用时的操作效果显示,修复禁用环境音效时仍然可以调整增益、新增预设的问题
|
- 优化音效设置-环境音效启用、禁用时的操作效果显示,修复禁用环境音效时仍然可以调整增益、新增预设的问题
|
||||||
- 过滤翻译歌词或罗马音歌词中只有“//”的行(#1499)
|
- 过滤翻译歌词或罗马音歌词中只有“//”的行(#1499)
|
||||||
- 点击打开歌单弹窗背景将不再自动关闭弹窗,防止选择输入框里的内容时意外关闭弹窗
|
- 点击打开歌单弹窗背景将不再自动关闭弹窗,防止选择输入框里的内容时意外关闭弹窗
|
||||||
|
- 优化数据传输逻辑,列表同步指令使用队列机制,保证列表同步操作的顺序
|
||||||
|
|
||||||
### 修复
|
### 修复
|
||||||
|
|
||||||
|
@ -21,6 +26,7 @@
|
||||||
- 修复某些tx源歌词因数据异常解析失败的问题
|
- 修复某些tx源歌词因数据异常解析失败的问题
|
||||||
- 修复windows平台下隐藏窗口后再显示时任务栏按钮丢失的问题
|
- 修复windows平台下隐藏窗口后再显示时任务栏按钮丢失的问题
|
||||||
- 修复首句歌词被提前播放的问题
|
- 修复首句歌词被提前播放的问题
|
||||||
|
- 修复潜在导致列表数据不同步的问题
|
||||||
|
|
||||||
### 其他
|
### 其他
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ export const DOWNLOAD_STATUS = {
|
||||||
export const QUALITYS = ['flac24bit', 'flac', 'wav', 'ape', '320k', '192k', '128k'] as const
|
export const QUALITYS = ['flac24bit', 'flac', 'wav', 'ape', '320k', '192k', '128k'] as const
|
||||||
|
|
||||||
export const SYNC_CODE = {
|
export const SYNC_CODE = {
|
||||||
helloMsg: 'Hello~::^-^::~v3~',
|
helloMsg: 'Hello~::^-^::~v4~',
|
||||||
idPrefix: 'OjppZDo6',
|
idPrefix: 'OjppZDo6',
|
||||||
authMsg: 'lx-music auth::',
|
authMsg: 'lx-music auth::',
|
||||||
authFailed: 'Auth failed',
|
authFailed: 'Auth failed',
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
declare namespace LX {
|
||||||
|
namespace Sync {
|
||||||
|
type ActionList = SyncAction<'list_data_overwrite', LX.List.ListActionDataOverwrite>
|
||||||
|
| SyncAction<'list_create', LX.List.ListActionAdd>
|
||||||
|
| SyncAction<'list_remove', LX.List.ListActionRemove>
|
||||||
|
| SyncAction<'list_update', LX.List.ListActionUpdate>
|
||||||
|
| SyncAction<'list_update_position', LX.List.ListActionUpdatePosition>
|
||||||
|
| SyncAction<'list_music_add', LX.List.ListActionMusicAdd>
|
||||||
|
| SyncAction<'list_music_move', LX.List.ListActionMusicMove>
|
||||||
|
| SyncAction<'list_music_remove', LX.List.ListActionMusicRemove>
|
||||||
|
| SyncAction<'list_music_update', LX.List.ListActionMusicUpdate>
|
||||||
|
| SyncAction<'list_music_update_position', LX.List.ListActionMusicUpdatePosition>
|
||||||
|
| SyncAction<'list_music_overwrite', LX.List.ListActionMusicOverwrite>
|
||||||
|
| SyncAction<'list_music_clear', LX.List.ListActionMusicClear>
|
||||||
|
|
||||||
|
type ServerActions = WarpPromiseRecord<{
|
||||||
|
onListSyncAction: (action: LX.Sync.ActionList) => void
|
||||||
|
}>
|
||||||
|
|
||||||
|
type ClientActions = WarpPromiseRecord<{
|
||||||
|
onListSyncAction: (action: LX.Sync.ActionList) => void
|
||||||
|
list_sync_get_md5: () => string
|
||||||
|
list_sync_get_sync_mode: () => Mode
|
||||||
|
list_sync_get_list_data: () => ListData
|
||||||
|
list_sync_set_list_data: (data: ListData) => void
|
||||||
|
list_sync_finished: () => void
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,3 +10,13 @@ type Modify<T, R> = Omit<T, keyof R> & R
|
||||||
type Actions<T extends { action: string, data?: any }> = {
|
type Actions<T extends { action: string, data?: any }> = {
|
||||||
[U in T as U['action']]: 'data' extends keyof U ? U['data'] : undefined
|
[U in T as U['action']]: 'data' extends keyof U ? U['data'] : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WarpPromiseValue<T> = T extends ((...args: infer P) => Promise<infer R>)
|
||||||
|
? ((...args: P) => Promise<R>)
|
||||||
|
: T extends ((...args: infer P2) => infer R2)
|
||||||
|
? ((...args: P2) => Promise<R2>)
|
||||||
|
: Promise<T>
|
||||||
|
|
||||||
|
type WarpPromiseRecord<T extends Record<string, any>> = {
|
||||||
|
[K in keyof T]: WarpPromiseValue<T[K]>
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { getSyncAuthKey, setSyncAuthKey } from '../data'
|
||||||
import { SYNC_CODE } from '@common/constants'
|
import { SYNC_CODE } from '@common/constants'
|
||||||
import log from '../log'
|
import log from '../log'
|
||||||
import { aesDecrypt, aesEncrypt, getComputerName, rsaDecrypt } from '../utils'
|
import { aesDecrypt, aesEncrypt, getComputerName, rsaDecrypt } from '../utils'
|
||||||
|
import { toMD5 } from '@common/utils/nodejs'
|
||||||
|
|
||||||
|
|
||||||
const hello = async(urlInfo: LX.Sync.Client.UrlInfo) => request(`${urlInfo.httpProtocol}//${urlInfo.hostPath}/hello`)
|
const hello = async(urlInfo: LX.Sync.Client.UrlInfo) => request(`${urlInfo.httpProtocol}//${urlInfo.hostPath}/hello`)
|
||||||
|
@ -25,7 +26,7 @@ const getServerId = async(urlInfo: LX.Sync.Client.UrlInfo) => request(`${urlInfo
|
||||||
})
|
})
|
||||||
|
|
||||||
const codeAuth = async(urlInfo: LX.Sync.Client.UrlInfo, serverId: string, authCode: string) => {
|
const codeAuth = async(urlInfo: LX.Sync.Client.UrlInfo, serverId: string, authCode: string) => {
|
||||||
let key = ''.padStart(16, Buffer.from(authCode).toString('hex'))
|
let key = toMD5(authCode).substring(0, 16)
|
||||||
// const iv = Buffer.from(key.split('').reverse().join('')).toString('base64')
|
// const iv = Buffer.from(key.split('').reverse().join('')).toString('base64')
|
||||||
key = Buffer.from(key).toString('base64')
|
key = Buffer.from(key).toString('base64')
|
||||||
let { publicKey, privateKey } = await generateRsaKey()
|
let { publicKey, privateKey } = await generateRsaKey()
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import WebSocket from 'ws'
|
import WebSocket from 'ws'
|
||||||
import { encryptMsg, decryptMsg } from './utils'
|
import { encryptMsg, decryptMsg } from './utils'
|
||||||
import * as modules from './modules'
|
import { modules, callObj } from './modules'
|
||||||
// import { action as commonAction } from '@root/store/modules/common'
|
// import { action as commonAction } from '@root/store/modules/common'
|
||||||
// import { getStore } from '@root/store'
|
// import { getStore } from '@root/store'
|
||||||
import registerSyncListHandler from './syncList'
|
// import registerSyncListHandler from './syncList'
|
||||||
import log from '../log'
|
import log from '../log'
|
||||||
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@common/constants'
|
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@common/constants'
|
||||||
import { dateFormat } from '@common/utils/common'
|
import { dateFormat } from '@common/utils/common'
|
||||||
import { aesEncrypt, getAddress } from '../utils'
|
import { aesEncrypt, getAddress } from '../utils'
|
||||||
import { sendClientStatus } from '@main/modules/winMain'
|
import { sendClientStatus } from '@main/modules/winMain'
|
||||||
|
import { createMsg2call } from 'message2call'
|
||||||
|
|
||||||
let status: LX.Sync.ClientStatus = {
|
let status: LX.Sync.ClientStatus = {
|
||||||
status: false,
|
status: false,
|
||||||
|
@ -31,8 +32,14 @@ export const sendSyncMessage = (message: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleConnection = (socket: LX.Sync.Client.Socket) => {
|
const handleConnection = (socket: LX.Sync.Client.Socket) => {
|
||||||
for (const moduleInit of Object.values(modules)) {
|
for (const { registerEvent } of Object.values(modules)) {
|
||||||
moduleInit(socket)
|
registerEvent(socket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDisconnection = () => {
|
||||||
|
for (const { unregisterEvent } of Object.values(modules)) {
|
||||||
|
unregisterEvent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +130,7 @@ const heartbeatTools = {
|
||||||
let client: LX.Sync.Client.Socket | null
|
let client: LX.Sync.Client.Socket | null
|
||||||
// let listSyncPromise: Promise<void>
|
// let listSyncPromise: Promise<void>
|
||||||
export const connect = (urlInfo: LX.Sync.Client.UrlInfo, keyInfo: LX.Sync.ClientKeyInfo) => {
|
export const connect = (urlInfo: LX.Sync.Client.UrlInfo, keyInfo: LX.Sync.ClientKeyInfo) => {
|
||||||
client = new WebSocket(`${urlInfo.wsProtocol}//${urlInfo.hostPath}?i=${encodeURIComponent(keyInfo.clientId)}&t=${encodeURIComponent(aesEncrypt(SYNC_CODE.msgConnect, keyInfo.key))}`, {
|
client = new WebSocket(`${urlInfo.wsProtocol}//${urlInfo.hostPath}/socket?i=${encodeURIComponent(keyInfo.clientId)}&t=${encodeURIComponent(aesEncrypt(SYNC_CODE.msgConnect, keyInfo.key))}`, {
|
||||||
}) as LX.Sync.Client.Socket
|
}) as LX.Sync.Client.Socket
|
||||||
client.data = {
|
client.data = {
|
||||||
keyInfo,
|
keyInfo,
|
||||||
|
@ -131,39 +138,67 @@ export const connect = (urlInfo: LX.Sync.Client.UrlInfo, keyInfo: LX.Sync.Client
|
||||||
}
|
}
|
||||||
heartbeatTools.connect(client)
|
heartbeatTools.connect(client)
|
||||||
|
|
||||||
// listSyncPromise = registerSyncListHandler(socket)
|
|
||||||
let events: Partial<{ [K in keyof LX.Sync.ActionSyncSendType]: Array<(data: LX.Sync.ActionSyncSendType[K]) => (void | Promise<void>)> }> = {}
|
|
||||||
let closeEvents: Array<(err: Error) => (void | Promise<void>)> = []
|
let closeEvents: Array<(err: Error) => (void | Promise<void>)> = []
|
||||||
|
|
||||||
|
const message2read = createMsg2call({
|
||||||
|
funcsObj: {
|
||||||
|
...callObj,
|
||||||
|
list_sync_finished() {
|
||||||
|
log.info('sync list success')
|
||||||
|
client!.isReady = true
|
||||||
|
handleConnection(client as LX.Sync.Client.Socket)
|
||||||
|
sendSyncStatus({
|
||||||
|
status: true,
|
||||||
|
message: '',
|
||||||
|
})
|
||||||
|
heartbeatTools.failedNum = 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
timeout: 120 * 1000,
|
||||||
|
sendMessage(data) {
|
||||||
|
void encryptMsg(keyInfo, JSON.stringify(data)).then((data) => {
|
||||||
|
client?.send(data)
|
||||||
|
}).catch((err) => {
|
||||||
|
log.error('encrypt msg error: ', err)
|
||||||
|
client?.close(SYNC_CLOSE_CODE.failed)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onCallBeforeParams(rawArgs) {
|
||||||
|
return [client, ...rawArgs]
|
||||||
|
},
|
||||||
|
onError(error, path, groupName) {
|
||||||
|
const name = groupName ?? ''
|
||||||
|
log.error(`sync call ${name} ${path.join('.')} error:`, error)
|
||||||
|
if (groupName == null) return
|
||||||
|
client?.close(SYNC_CLOSE_CODE.failed)
|
||||||
|
sendSyncStatus({
|
||||||
|
status: false,
|
||||||
|
message: error.message,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
client.remoteSyncList = message2read.createSyncRemote('list')
|
||||||
|
|
||||||
client.addEventListener('message', ({ data }) => {
|
client.addEventListener('message', ({ data }) => {
|
||||||
if (data == 'ping') return
|
if (data == 'ping') return
|
||||||
if (typeof data === 'string') {
|
if (typeof data === 'string') {
|
||||||
let syncData: LX.Sync.ActionSync
|
void decryptMsg(keyInfo, data).then((data) => {
|
||||||
|
let syncData: LX.Sync.ServerActions
|
||||||
try {
|
try {
|
||||||
syncData = JSON.parse(decryptMsg(keyInfo, data))
|
syncData = JSON.parse(data)
|
||||||
} catch {
|
} catch (err) {
|
||||||
|
log.error('parse msg error: ', err)
|
||||||
|
client?.close(SYNC_CLOSE_CODE.failed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const handlers = events[syncData.action]
|
message2read.onMessage(syncData)
|
||||||
if (handlers) {
|
}).catch((error) => {
|
||||||
// @ts-expect-error
|
log.error('decrypt msg error: ', error)
|
||||||
for (const handler of handlers) void handler(syncData.data)
|
client?.close(SYNC_CLOSE_CODE.failed)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
client.onRemoteEvent = function(eventName, handler) {
|
|
||||||
let eventArr = events[eventName]
|
|
||||||
if (!eventArr) events[eventName] = eventArr = []
|
|
||||||
// let eventArr = events.get(eventName)
|
|
||||||
// if (!eventArr) events.set(eventName, eventArr = [])
|
|
||||||
eventArr.push(handler)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
eventArr!.splice(eventArr!.indexOf(handler), 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
client.sendData = function(eventName, data, callback) {
|
|
||||||
client?.send(encryptMsg(keyInfo, JSON.stringify({ action: eventName, data })), callback)
|
|
||||||
}
|
|
||||||
client.onClose = function(handler: typeof closeEvents[number]) {
|
client.onClose = function(handler: typeof closeEvents[number]) {
|
||||||
closeEvents.push(handler)
|
closeEvents.push(handler)
|
||||||
return () => {
|
return () => {
|
||||||
|
@ -171,6 +206,7 @@ export const connect = (urlInfo: LX.Sync.Client.UrlInfo, keyInfo: LX.Sync.Client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initMessage = 'Wait syncing...'
|
||||||
client.addEventListener('open', () => {
|
client.addEventListener('open', () => {
|
||||||
log.info('connect')
|
log.info('connect')
|
||||||
// const store = getStore()
|
// const store = getStore()
|
||||||
|
@ -178,39 +214,14 @@ export const connect = (urlInfo: LX.Sync.Client.UrlInfo, keyInfo: LX.Sync.Client
|
||||||
client!.isReady = false
|
client!.isReady = false
|
||||||
sendSyncStatus({
|
sendSyncStatus({
|
||||||
status: false,
|
status: false,
|
||||||
message: 'Wait syncing...',
|
message: initMessage,
|
||||||
})
|
|
||||||
void registerSyncListHandler(client as LX.Sync.Client.Socket).then(() => {
|
|
||||||
log.info('sync list success')
|
|
||||||
handleConnection(client as LX.Sync.Client.Socket)
|
|
||||||
log.info('register list sync service success')
|
|
||||||
client!.isReady = true
|
|
||||||
heartbeatTools.failedNum = 0
|
|
||||||
sendSyncStatus({
|
|
||||||
status: true,
|
|
||||||
message: '',
|
|
||||||
})
|
|
||||||
}).catch(err => {
|
|
||||||
if (err.message == 'closed') {
|
|
||||||
sendSyncStatus({
|
|
||||||
status: false,
|
|
||||||
message: '',
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
console.log(err)
|
|
||||||
log.r_error(err.stack)
|
|
||||||
sendSyncStatus({
|
|
||||||
status: false,
|
|
||||||
message: err.message,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
client.addEventListener('close', ({ code }) => {
|
client.addEventListener('close', ({ code }) => {
|
||||||
const err = new Error('closed')
|
const err = new Error('closed')
|
||||||
for (const handler of closeEvents) void handler(err)
|
for (const handler of closeEvents) void handler(err)
|
||||||
|
handleDisconnection()
|
||||||
closeEvents = []
|
closeEvents = []
|
||||||
events = {}
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case SYNC_CLOSE_CODE.normal:
|
case SYNC_CLOSE_CODE.normal:
|
||||||
// case SYNC_CLOSE_CODE.failed:
|
// case SYNC_CLOSE_CODE.failed:
|
||||||
|
@ -218,6 +229,15 @@ export const connect = (urlInfo: LX.Sync.Client.UrlInfo, keyInfo: LX.Sync.Client
|
||||||
status: false,
|
status: false,
|
||||||
message: '',
|
message: '',
|
||||||
})
|
})
|
||||||
|
break
|
||||||
|
case SYNC_CLOSE_CODE.failed:
|
||||||
|
if (!status.message || status.message == initMessage) {
|
||||||
|
sendSyncStatus({
|
||||||
|
status: false,
|
||||||
|
message: 'failed',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
client.addEventListener('error', ({ message }) => {
|
client.addEventListener('error', ({ message }) => {
|
||||||
|
|
|
@ -1 +1,10 @@
|
||||||
export { default as list } from './list'
|
import * as list from './list'
|
||||||
|
// export * as theme from './theme'
|
||||||
|
|
||||||
|
|
||||||
|
export const callObj = Object.assign({}, list.handler)
|
||||||
|
|
||||||
|
|
||||||
|
export const modules = {
|
||||||
|
list,
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { handleRemoteListAction } from '@main/modules/sync/utils'
|
||||||
|
import { getLocalListData, setLocalListData } from '../../../utils'
|
||||||
|
import { toMD5 } from '@common/utils/nodejs'
|
||||||
|
import { removeSelectModeListener, sendCloseSelectMode, sendSelectMode } from '@main/modules/winMain'
|
||||||
|
import log from '@main/modules/sync/log'
|
||||||
|
|
||||||
|
const logInfo = (eventName: string, success = false) => {
|
||||||
|
log.info(`[${eventName}]${eventName.replace('list:sync:list_sync_', '').replaceAll('_', ' ')}${success ? ' success' : ''}`)
|
||||||
|
}
|
||||||
|
// const logError = (eventName: string, err: Error) => {
|
||||||
|
// log.error(`[${eventName}]${eventName.replace('list:sync:list_sync_', '').replaceAll('_', ' ')} error: ${err.message}`)
|
||||||
|
// }
|
||||||
|
|
||||||
|
export const onListSyncAction = async(socket: LX.Sync.Client.Socket, action: LX.Sync.ActionList) => {
|
||||||
|
if (!socket.isReady) return
|
||||||
|
await handleRemoteListAction(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const list_sync_get_md5 = async(socket: LX.Sync.Client.Socket) => {
|
||||||
|
logInfo('list:sync:list_sync_get_md5')
|
||||||
|
return toMD5(JSON.stringify(await getLocalListData()))
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSyncMode = async(socket: LX.Sync.Client.Socket): Promise<LX.Sync.Mode> => new Promise((resolve, reject) => {
|
||||||
|
const handleDisconnect = (err: Error) => {
|
||||||
|
sendCloseSelectMode()
|
||||||
|
removeSelectModeListener()
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
let removeEventClose = socket.onClose(handleDisconnect)
|
||||||
|
sendSelectMode(socket.data.keyInfo.serverName, (mode) => {
|
||||||
|
if (mode == null) {
|
||||||
|
reject(new Error('cancel'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resolve(mode)
|
||||||
|
removeSelectModeListener()
|
||||||
|
removeEventClose()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
export const list_sync_get_sync_mode = async(socket: LX.Sync.Client.Socket) => {
|
||||||
|
return getSyncMode(socket)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const list_sync_get_list_data = async(socket: LX.Sync.Client.Socket) => {
|
||||||
|
logInfo('list:sync:list_sync_get_list_data')
|
||||||
|
return getLocalListData()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const list_sync_set_list_data = async(socket: LX.Sync.Client.Socket, data: LX.Sync.ListData) => {
|
||||||
|
logInfo('list:sync:list_sync_set_list_data')
|
||||||
|
await setLocalListData(data)
|
||||||
|
}
|
|
@ -1,7 +1,4 @@
|
||||||
import initOn from './on'
|
|
||||||
import initSend from './send'
|
|
||||||
|
|
||||||
export default (socket: LX.Sync.Client.Socket) => {
|
export * as handler from './handler'
|
||||||
initOn(socket)
|
|
||||||
initSend(socket)
|
export * from './localEvent'
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { registerListActionEvent } from '@main/modules/sync/utils'
|
||||||
|
|
||||||
|
let unregisterLocalListAction: (() => void) | null
|
||||||
|
|
||||||
|
export const registerEvent = (socket: LX.Sync.Client.Socket) => {
|
||||||
|
// socket = _socket
|
||||||
|
// socket.onClose(() => {
|
||||||
|
// unregisterLocalListAction?.()
|
||||||
|
// unregisterLocalListAction = null
|
||||||
|
// })
|
||||||
|
unregisterLocalListAction = registerListActionEvent((action) => {
|
||||||
|
if (!socket?.isReady) return
|
||||||
|
void socket.remoteSyncList.onListSyncAction(action)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const unregisterEvent = () => {
|
||||||
|
unregisterLocalListAction?.()
|
||||||
|
unregisterLocalListAction = null
|
||||||
|
}
|
|
@ -1,9 +0,0 @@
|
||||||
import { handleRemoteListAction } from '@main/modules/sync/utils'
|
|
||||||
|
|
||||||
export default (socket: LX.Sync.Client.Socket) => {
|
|
||||||
socket.onRemoteEvent('list:sync:action', (action) => {
|
|
||||||
if (!socket.isReady) return
|
|
||||||
void handleRemoteListAction(action)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
import { registerListActionEvent } from '@main/modules/sync/utils'
|
|
||||||
|
|
||||||
let socket: LX.Sync.Client.Socket | null
|
|
||||||
let unregisterLocalListAction: (() => void) | null
|
|
||||||
|
|
||||||
|
|
||||||
const sendListAction = (action: LX.Sync.ActionList) => {
|
|
||||||
// console.log('sendListAction', action.action)
|
|
||||||
if (!socket?.isReady) return
|
|
||||||
socket.sendData('list:sync:action', action)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default (_socket: LX.Sync.Client.Socket) => {
|
|
||||||
socket = _socket
|
|
||||||
socket.onClose(() => {
|
|
||||||
socket = null
|
|
||||||
unregisterLocalListAction?.()
|
|
||||||
unregisterLocalListAction = null
|
|
||||||
})
|
|
||||||
unregisterLocalListAction = registerListActionEvent(sendListAction)
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
import log from '../log'
|
|
||||||
import { getLocalListData, setLocalListData } from '../utils'
|
|
||||||
import { toMD5 } from '@common/utils/nodejs'
|
|
||||||
import { removeSelectModeListener, sendCloseSelectMode, sendSelectMode } from '@main/modules/winMain'
|
|
||||||
import { SYNC_CLOSE_CODE } from '@common/constants'
|
|
||||||
|
|
||||||
const logInfo = (eventName: keyof LX.Sync.ActionSyncSendType, success = false) => {
|
|
||||||
log.info(`[${eventName as string}]${eventName.replace('list:sync:list_sync_', '').replaceAll('_', ' ')}${success ? ' success' : ''}`)
|
|
||||||
}
|
|
||||||
const logError = (eventName: keyof LX.Sync.ActionSyncSendType, err: Error) => {
|
|
||||||
log.error(`[${eventName as string}]${eventName.replace('list:sync:list_sync_', '').replaceAll('_', ' ')} error: ${err.message}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async(socket: LX.Sync.Client.Socket) => new Promise<void>((resolve, reject) => {
|
|
||||||
let listenEvents: Array<() => void> = []
|
|
||||||
const unregisterEvents = () => {
|
|
||||||
while (listenEvents.length) listenEvents.shift()?.()
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.onClose(() => {
|
|
||||||
unregisterEvents()
|
|
||||||
sendCloseSelectMode()
|
|
||||||
removeSelectModeListener()
|
|
||||||
reject(new Error('closed'))
|
|
||||||
})
|
|
||||||
listenEvents.push(socket.onRemoteEvent('list:sync:list_sync_get_md5', async() => {
|
|
||||||
logInfo('list:sync:list_sync_get_md5')
|
|
||||||
const md5 = toMD5(JSON.stringify(await getLocalListData()))
|
|
||||||
socket?.sendData('list:sync:list_sync_get_md5', md5, (err) => {
|
|
||||||
if (err) {
|
|
||||||
logError('list:sync:list_sync_get_md5', err)
|
|
||||||
socket.close(SYNC_CLOSE_CODE.failed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logInfo('list:sync:list_sync_get_md5', true)
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
listenEvents.push(socket.onRemoteEvent('list:sync:list_sync_get_list_data', async() => {
|
|
||||||
logInfo('list:sync:list_sync_get_list_data')
|
|
||||||
socket?.sendData('list:sync:list_sync_get_list_data', await getLocalListData(), (err) => {
|
|
||||||
if (err) {
|
|
||||||
logError('list:sync:list_sync_get_list_data', err)
|
|
||||||
socket.close(SYNC_CLOSE_CODE.failed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logInfo('list:sync:list_sync_get_list_data', true)
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
listenEvents.push(socket.onRemoteEvent('list:sync:list_sync_get_sync_mode', async() => {
|
|
||||||
logInfo('list:sync:list_sync_get_sync_mode')
|
|
||||||
sendSelectMode(socket.data.keyInfo.serverName, (mode) => {
|
|
||||||
removeSelectModeListener()
|
|
||||||
socket?.sendData('list:sync:list_sync_get_sync_mode', mode, (err) => {
|
|
||||||
if (err) {
|
|
||||||
logError('list:sync:list_sync_get_sync_mode', err)
|
|
||||||
socket.close(SYNC_CLOSE_CODE.failed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logInfo('list:sync:list_sync_get_sync_mode', true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
listenEvents.push(socket.onRemoteEvent('list:sync:list_sync_set_data', async(data) => {
|
|
||||||
logInfo('list:sync:list_sync_set_data')
|
|
||||||
await setLocalListData(data)
|
|
||||||
logInfo('list:sync:list_sync_set_data', true)
|
|
||||||
}))
|
|
||||||
listenEvents.push(socket.onRemoteEvent('list:sync:finished', async() => {
|
|
||||||
unregisterEvents()
|
|
||||||
resolve()
|
|
||||||
logInfo('list:sync:finished', true)
|
|
||||||
}))
|
|
||||||
})
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { generateKeyPair } from 'node:crypto'
|
import { generateKeyPair } from 'node:crypto'
|
||||||
import { httpFetch, type RequestOptions } from '@main/utils/request'
|
import { httpFetch, type RequestOptions } from '@main/utils/request'
|
||||||
|
import { decodeData, encodeData } from '../utils'
|
||||||
|
|
||||||
export const request = async(url: string, options: RequestOptions = { }) => {
|
export const request = async(url: string, options: RequestOptions = { }) => {
|
||||||
return httpFetch(url, {
|
return httpFetch(url, {
|
||||||
|
@ -74,15 +75,14 @@ export const generateRsaKey = async() => new Promise<{ publicKey: string, privat
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const encryptMsg = async(keyInfo: LX.Sync.ClientKeyInfo, msg: string): Promise<string> => {
|
||||||
export const encryptMsg = (keyInfo: LX.Sync.ClientKeyInfo, msg: string): string => {
|
return encodeData(msg)
|
||||||
return msg
|
|
||||||
// if (!keyInfo) return ''
|
// if (!keyInfo) return ''
|
||||||
// return aesEncrypt(msg, keyInfo.key, keyInfo.iv)
|
// return aesEncrypt(msg, keyInfo.key, keyInfo.iv)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const decryptMsg = (keyInfo: LX.Sync.ClientKeyInfo, enMsg: string): string => {
|
export const decryptMsg = async(keyInfo: LX.Sync.ClientKeyInfo, enMsg: string): Promise<string> => {
|
||||||
return enMsg
|
return decodeData(enMsg)
|
||||||
// if (!keyInfo) return ''
|
// if (!keyInfo) return ''
|
||||||
// let msg = ''
|
// let msg = ''
|
||||||
// try {
|
// try {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import querystring from 'node:querystring'
|
||||||
import { getIP } from './utils'
|
import { getIP } from './utils'
|
||||||
import { createClientKeyInfo, getClientKeyInfo, saveClientKeyInfo } from '../data'
|
import { createClientKeyInfo, getClientKeyInfo, saveClientKeyInfo } from '../data'
|
||||||
import { aesDecrypt, aesEncrypt, getComputerName, rsaEncrypt } from '../utils'
|
import { aesDecrypt, aesEncrypt, getComputerName, rsaEncrypt } from '../utils'
|
||||||
|
import { toMD5 } from '@common/utils/nodejs'
|
||||||
|
|
||||||
const requestIps = new Map<string, number>()
|
const requestIps = new Map<string, number>()
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ const verifyByKey = (encryptMsg: string, userId: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const verifyByCode = (encryptMsg: string, password: string) => {
|
const verifyByCode = (encryptMsg: string, password: string) => {
|
||||||
let key = ''.padStart(16, Buffer.from(password).toString('hex'))
|
let key = toMD5(password).substring(0, 16)
|
||||||
// const iv = Buffer.from(key.split('').reverse().join('')).toString('base64')
|
// const iv = Buffer.from(key.split('').reverse().join('')).toString('base64')
|
||||||
key = Buffer.from(key).toString('base64')
|
key = Buffer.from(key).toString('base64')
|
||||||
// console.log(req.headers.m, authCode, key)
|
// console.log(req.headers.m, authCode, key)
|
||||||
|
|
|
@ -1 +1,10 @@
|
||||||
export * as list from './list'
|
import * as list from './list'
|
||||||
|
// export * as theme from './theme'
|
||||||
|
|
||||||
|
|
||||||
|
export const callObj = Object.assign({}, list.handler)
|
||||||
|
|
||||||
|
|
||||||
|
export const modules = {
|
||||||
|
list,
|
||||||
|
}
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
// import { throttle } from '@common/utils/common'
|
|
||||||
// import { sendSyncActionList } from '@main/modules/winMain'
|
|
||||||
import { SYNC_CLOSE_CODE } from '@common/constants'
|
|
||||||
import { updateDeviceSnapshotKey } from '../../data'
|
|
||||||
import { handleRemoteListAction, registerListActionEvent } from '../../utils'
|
|
||||||
import { createSnapshot, encryptMsg, getCurrentListInfoKey } from '../utils'
|
|
||||||
|
|
||||||
let wss: LX.Sync.Server.SocketServer | null
|
|
||||||
let removeListener: (() => void) | null
|
|
||||||
|
|
||||||
// type listAction = 'list:action'
|
|
||||||
|
|
||||||
// const addMusic = (orderId, callback) => {
|
|
||||||
// // ...
|
|
||||||
// }
|
|
||||||
|
|
||||||
const broadcast = async(key: string, data: any, excludeIds: string[] = []) => {
|
|
||||||
if (!wss) return
|
|
||||||
const dataStr = JSON.stringify({ action: 'list:sync:action', data })
|
|
||||||
for (const socket of wss.clients) {
|
|
||||||
if (excludeIds.includes(socket.keyInfo.clientId) || !socket.isReady) continue
|
|
||||||
socket.send(encryptMsg(socket.keyInfo, dataStr), (err) => {
|
|
||||||
if (err) {
|
|
||||||
socket.close(SYNC_CLOSE_CODE.failed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
updateDeviceSnapshotKey(socket.keyInfo, key)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const sendListAction = async(action: LX.Sync.ActionList) => {
|
|
||||||
console.log('sendListAction', action.action)
|
|
||||||
// io.sockets
|
|
||||||
await broadcast(await getCurrentListInfoKey(), action)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const registerListHandler = (_wss: LX.Sync.Server.SocketServer, socket: LX.Sync.Server.Socket) => {
|
|
||||||
if (!wss) {
|
|
||||||
wss = _wss
|
|
||||||
removeListener = registerListActionEvent(sendListAction)
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.onRemoteEvent('list:sync:action', (action) => {
|
|
||||||
if (!socket.isReady) return
|
|
||||||
// console.log(msg)
|
|
||||||
void handleRemoteListAction(action).then(updated => {
|
|
||||||
if (!updated) return
|
|
||||||
void createSnapshot().then(key => {
|
|
||||||
if (!key) return
|
|
||||||
updateDeviceSnapshotKey(socket.keyInfo, key)
|
|
||||||
void broadcast(key, action, [socket.keyInfo.clientId])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
// socket.broadcast.emit('list:action', { action: 'list_remove', data: { id: 'default', index: 0 } })
|
|
||||||
})
|
|
||||||
|
|
||||||
// socket.on('list:add', addMusic)
|
|
||||||
}
|
|
||||||
export const unregisterListHandler = () => {
|
|
||||||
wss = null
|
|
||||||
|
|
||||||
if (removeListener) {
|
|
||||||
removeListener()
|
|
||||||
removeListener = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
// import { throttle } from '@common/utils/common'
|
||||||
|
// import { sendSyncActionList } from '@main/modules/winMain'
|
||||||
|
// import { SYNC_CLOSE_CODE } from '@common/constants'
|
||||||
|
import { updateDeviceSnapshotKey } from '@main/modules/sync/data'
|
||||||
|
import { handleRemoteListAction } from '../../../utils'
|
||||||
|
import { createSnapshot } from '../../utils'
|
||||||
|
|
||||||
|
// let wss: LX.Sync.Server.SocketServer | null
|
||||||
|
// let removeListener: (() => void) | null
|
||||||
|
|
||||||
|
// type listAction = 'list:action'
|
||||||
|
|
||||||
|
// const addMusic = (orderId, callback) => {
|
||||||
|
// // ...
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const broadcast = async(key: string, data: any, excludeIds: string[] = []) => {
|
||||||
|
// if (!wss) return
|
||||||
|
// const dataStr = JSON.stringify({ action: 'list:sync:action', data })
|
||||||
|
// const clients = Array.from(wss.clients).filter(socket => !excludeIds.includes(socket.keyInfo.clientId) && socket.isReady)
|
||||||
|
// if (!clients.length) return
|
||||||
|
// const enData = await encryptMsg(null, dataStr)
|
||||||
|
// for (const socket of clients) {
|
||||||
|
// if (excludeIds.includes(socket.keyInfo.clientId) || !socket.isReady) continue
|
||||||
|
// socket.send(enData, (err) => {
|
||||||
|
// if (err) {
|
||||||
|
// socket.close(SYNC_CLOSE_CODE.failed)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// updateDeviceSnapshotKey(socket.keyInfo, key)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const sendListAction = async(action: LX.Sync.ActionList) => {
|
||||||
|
// console.log('sendListAction', action.action)
|
||||||
|
// // io.sockets
|
||||||
|
// await broadcast(await getCurrentListInfoKey(), action)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export const registerListHandler = (_wss: LX.Sync.Server.SocketServer, socket: LX.Sync.Server.Socket) => {
|
||||||
|
// if (!wss) {
|
||||||
|
// wss = _wss
|
||||||
|
// removeListener = registerListActionEvent(sendListAction)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// socket.onRemoteEvent('list:sync:action', (action) => {
|
||||||
|
// if (!socket.isReady) return
|
||||||
|
// // console.log(msg)
|
||||||
|
// void handleRemoteListAction(action).then(updated => {
|
||||||
|
// if (!updated) return
|
||||||
|
// void createSnapshot().then(key => {
|
||||||
|
// if (!key) return
|
||||||
|
// updateDeviceSnapshotKey(socket.keyInfo, key)
|
||||||
|
// void broadcast(key, action, [socket.keyInfo.clientId])
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
// // socket.broadcast.emit('list:action', { action: 'list_remove', data: { id: 'default', index: 0 } })
|
||||||
|
// })
|
||||||
|
|
||||||
|
// // socket.on('list:add', addMusic)
|
||||||
|
// }
|
||||||
|
// export const unregisterListHandler = () => {
|
||||||
|
// wss = null
|
||||||
|
|
||||||
|
// if (removeListener) {
|
||||||
|
// removeListener()
|
||||||
|
// removeListener = null
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
export const onListSyncAction = async(socket: LX.Sync.Server.Socket, action: LX.Sync.ActionList) => {
|
||||||
|
await handleRemoteListAction(action).then(updated => {
|
||||||
|
if (!updated) return
|
||||||
|
console.log(updated)
|
||||||
|
void createSnapshot().then(key => {
|
||||||
|
if (!key) return
|
||||||
|
updateDeviceSnapshotKey(socket.keyInfo, key)
|
||||||
|
const currentId = socket.keyInfo.clientId
|
||||||
|
socket.broadcast((client) => {
|
||||||
|
if (client.keyInfo.clientId == currentId || !client.isReady) return
|
||||||
|
void client.remoteSyncList.onListSyncAction(action)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
export * as handler from './handler'
|
||||||
|
export { default as sync } from './sync'
|
||||||
|
|
||||||
|
export * from './localEvent'
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { updateDeviceSnapshotKey } from '@main/modules/sync/data'
|
||||||
|
import { registerListActionEvent } from '../../../utils'
|
||||||
|
import { getCurrentListInfoKey } from '../../utils'
|
||||||
|
|
||||||
|
// let socket: LX.Sync.Server.Socket | null
|
||||||
|
let unregisterLocalListAction: (() => void) | null
|
||||||
|
|
||||||
|
|
||||||
|
const sendListAction = async(wss: LX.Sync.Server.SocketServer, action: LX.Sync.ActionList) => {
|
||||||
|
// console.log('sendListAction', action.action)
|
||||||
|
const key = await getCurrentListInfoKey()
|
||||||
|
for (const client of wss.clients) {
|
||||||
|
if (!client.isReady) return
|
||||||
|
void client.remoteSyncList.onListSyncAction(action).then(() => {
|
||||||
|
updateDeviceSnapshotKey(client.keyInfo, key)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const registerEvent = (wss: LX.Sync.Server.SocketServer) => {
|
||||||
|
// socket = _socket
|
||||||
|
// socket.onClose(() => {
|
||||||
|
// unregisterLocalListAction?.()
|
||||||
|
// unregisterLocalListAction = null
|
||||||
|
// })
|
||||||
|
unregisterLocalListAction = registerListActionEvent((action) => {
|
||||||
|
void sendListAction(wss, action)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const unregisterEvent = () => {
|
||||||
|
unregisterLocalListAction?.()
|
||||||
|
unregisterLocalListAction = null
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
import { SYNC_CLOSE_CODE } from '@common/constants'
|
import { SYNC_CLOSE_CODE } from '@common/constants'
|
||||||
|
import { getLocalListData, setLocalListData } from '@main/modules/sync/utils'
|
||||||
import { removeSelectModeListener, sendCloseSelectMode, sendSelectMode } from '@main/modules/winMain'
|
import { removeSelectModeListener, sendCloseSelectMode, sendSelectMode } from '@main/modules/winMain'
|
||||||
import { getSnapshot, updateDeviceSnapshotKey } from '../data'
|
import { createSnapshot, getCurrentListInfoKey } from '../../utils'
|
||||||
import { getLocalListData, setLocalListData } from '../utils'
|
import { getSnapshot, updateDeviceSnapshotKey } from '@main/modules/sync/data'
|
||||||
import { createSnapshot, encryptMsg, getCurrentListInfoKey } from './utils'
|
|
||||||
|
|
||||||
|
|
||||||
const handleSetLocalListData = async(listData: LX.Sync.ListData) => {
|
const handleSetLocalListData = async(listData: LX.Sync.ListData) => {
|
||||||
|
@ -12,7 +12,7 @@ const handleSetLocalListData = async(listData: LX.Sync.ListData) => {
|
||||||
|
|
||||||
// type ListInfoType = LX.List.UserListInfoFull | LX.List.MyDefaultListInfoFull | LX.List.MyLoveListInfoFull
|
// type ListInfoType = LX.List.UserListInfoFull | LX.List.MyDefaultListInfoFull | LX.List.MyLoveListInfoFull
|
||||||
|
|
||||||
let wss: LX.Sync.Server.SocketServer | null
|
// let wss: LX.Sync.Server.SocketServer | null
|
||||||
let syncingId: string | null = null
|
let syncingId: string | null = null
|
||||||
const wait = async(time = 1000) => await new Promise((resolve, reject) => setTimeout(resolve, time))
|
const wait = async(time = 1000) => await new Promise((resolve, reject) => setTimeout(resolve, time))
|
||||||
|
|
||||||
|
@ -24,37 +24,14 @@ const patchListData = (listData: Partial<LX.Sync.ListData>): LX.Sync.ListData =>
|
||||||
}, listData)
|
}, listData)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getRemoteListData = async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.ListData> => await new Promise((resolve, reject) => {
|
const getRemoteListData = async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.ListData> => {
|
||||||
console.log('getRemoteListData')
|
console.log('getRemoteListData')
|
||||||
let removeEventClose = socket.onClose(reject)
|
return patchListData(await socket.remoteSyncList.list_sync_get_list_data())
|
||||||
let removeEvent = socket.onRemoteEvent('list:sync:list_sync_get_list_data', (listData) => {
|
}
|
||||||
resolve(patchListData(listData))
|
|
||||||
removeEventClose()
|
|
||||||
removeEvent()
|
|
||||||
})
|
|
||||||
socket.sendData('list:sync:list_sync_get_list_data', undefined, (err) => {
|
|
||||||
if (!err) return
|
|
||||||
reject(err)
|
|
||||||
removeEventClose()
|
|
||||||
removeEvent()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const getRemoteListMD5 = async(socket: LX.Sync.Server.Socket): Promise<string> => await new Promise((resolve, reject) => {
|
|
||||||
let removeEventClose = socket.onClose(reject)
|
|
||||||
let removeEvent = socket.onRemoteEvent('list:sync:list_sync_get_md5', (md5) => {
|
|
||||||
resolve(md5)
|
|
||||||
removeEventClose()
|
|
||||||
removeEvent()
|
|
||||||
})
|
|
||||||
socket.sendData('list:sync:list_sync_get_md5', undefined, (err) => {
|
|
||||||
if (!err) return
|
|
||||||
reject(err)
|
|
||||||
removeEventClose()
|
|
||||||
removeEvent()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
|
const getRemoteListMD5 = async(socket: LX.Sync.Server.Socket): Promise<string> => {
|
||||||
|
return socket.remoteSyncList.list_sync_get_md5()
|
||||||
|
}
|
||||||
|
|
||||||
const getSyncMode = async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.Mode> => new Promise((resolve, reject) => {
|
const getSyncMode = async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.Mode> => new Promise((resolve, reject) => {
|
||||||
const handleDisconnect = (err: Error) => {
|
const handleDisconnect = (err: Error) => {
|
||||||
|
@ -62,53 +39,38 @@ const getSyncMode = async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.Mode>
|
||||||
removeSelectModeListener()
|
removeSelectModeListener()
|
||||||
reject(err)
|
reject(err)
|
||||||
}
|
}
|
||||||
sendSelectMode(socket.keyInfo.deviceName, (mode) => {
|
|
||||||
removeSelectModeListener()
|
|
||||||
removeEventClose()
|
|
||||||
resolve(mode)
|
|
||||||
})
|
|
||||||
let removeEventClose = socket.onClose(handleDisconnect)
|
let removeEventClose = socket.onClose(handleDisconnect)
|
||||||
})
|
sendSelectMode(socket.keyInfo.deviceName, (mode) => {
|
||||||
|
if (mode == null) {
|
||||||
const finishedSync = async(socket: LX.Sync.Server.Socket) => new Promise<void>((resolve, reject) => {
|
reject(new Error('cancel'))
|
||||||
socket.sendData('list:sync:finished', undefined, (err) => {
|
|
||||||
if (err) reject(err)
|
|
||||||
else resolve()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const sendDataPromise = async(socket: LX.Sync.Server.Socket, dataStr: string, key: string) => new Promise<void>((resolve, reject) => {
|
|
||||||
socket.send(encryptMsg(socket.keyInfo, dataStr), (err) => {
|
|
||||||
if (err) {
|
|
||||||
socket.close(SYNC_CLOSE_CODE.failed)
|
|
||||||
resolve()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
updateDeviceSnapshotKey(socket.keyInfo, key)
|
resolve(mode)
|
||||||
resolve()
|
removeSelectModeListener()
|
||||||
|
removeEventClose()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
const overwriteRemoteListData = async(listData: LX.Sync.ListData, key: string, excludeIds: string[] = []) => {
|
|
||||||
if (!wss) return
|
const finishedSync = async(socket: LX.Sync.Server.Socket) => {
|
||||||
const dataStr = JSON.stringify({ action: 'list:sync:action', data: { action: 'list_data_overwrite', data: listData } })
|
await socket.remoteSyncList.list_sync_finished()
|
||||||
|
}
|
||||||
|
|
||||||
|
const overwriteRemoteListData = async(socket: LX.Sync.Server.Socket, listData: LX.Sync.ListData, key: string, excludeIds: string[] = []) => {
|
||||||
|
const action = { action: 'list_data_overwrite', data: listData } as const
|
||||||
const tasks: Array<Promise<void>> = []
|
const tasks: Array<Promise<void>> = []
|
||||||
for (const socket of wss.clients) {
|
socket.broadcast((client) => {
|
||||||
if (excludeIds.includes(socket.keyInfo.clientId) || !socket.isReady) continue
|
if (excludeIds.includes(client.keyInfo.clientId) || !client.isReady) return
|
||||||
tasks.push(sendDataPromise(socket, dataStr, key))
|
tasks.push(client.remoteSyncList.onListSyncAction(action).then(() => {
|
||||||
}
|
updateDeviceSnapshotKey(socket.keyInfo, key)
|
||||||
|
}))
|
||||||
|
})
|
||||||
if (!tasks.length) return
|
if (!tasks.length) return
|
||||||
await Promise.all(tasks)
|
await Promise.all(tasks)
|
||||||
}
|
}
|
||||||
const setRemotelList = async(socket: LX.Sync.Server.Socket, listData: LX.Sync.ListData, key: string): Promise<void> => new Promise((resolve, reject) => {
|
const setRemotelList = async(socket: LX.Sync.Server.Socket, listData: LX.Sync.ListData, key: string): Promise<void> => {
|
||||||
socket.sendData('list:sync:list_sync_set_data', listData, (err) => {
|
await socket.remoteSyncList.list_sync_set_list_data(listData)
|
||||||
if (err) {
|
|
||||||
reject(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
updateDeviceSnapshotKey(socket.keyInfo, key)
|
updateDeviceSnapshotKey(socket.keyInfo, key)
|
||||||
resolve()
|
}
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
type UserDataObj = Map<string, LX.List.UserListInfoFull>
|
type UserDataObj = Map<string, LX.List.UserListInfoFull>
|
||||||
const createUserListDataObj = (listData: LX.Sync.ListData): UserDataObj => {
|
const createUserListDataObj = (listData: LX.Sync.ListData): UserDataObj => {
|
||||||
|
@ -268,7 +230,7 @@ const handleSyncList = async(socket: LX.Sync.Server.Socket) => {
|
||||||
let key
|
let key
|
||||||
if (requiredUpdateLocalListData) {
|
if (requiredUpdateLocalListData) {
|
||||||
key = await handleSetLocalListData(mergedList)
|
key = await handleSetLocalListData(mergedList)
|
||||||
await overwriteRemoteListData(mergedList, key, [socket.keyInfo.clientId])
|
await overwriteRemoteListData(socket, mergedList, key, [socket.keyInfo.clientId])
|
||||||
if (!requiredUpdateRemoteListData) updateDeviceSnapshotKey(socket.keyInfo, key)
|
if (!requiredUpdateRemoteListData) updateDeviceSnapshotKey(socket.keyInfo, key)
|
||||||
}
|
}
|
||||||
if (requiredUpdateRemoteListData) {
|
if (requiredUpdateRemoteListData) {
|
||||||
|
@ -282,7 +244,7 @@ const handleSyncList = async(socket: LX.Sync.Server.Socket) => {
|
||||||
let key: string
|
let key: string
|
||||||
if (remoteListData.defaultList.length || remoteListData.loveList.length || remoteListData.userList.length) {
|
if (remoteListData.defaultList.length || remoteListData.loveList.length || remoteListData.userList.length) {
|
||||||
key = await handleSetLocalListData(remoteListData)
|
key = await handleSetLocalListData(remoteListData)
|
||||||
await overwriteRemoteListData(remoteListData, key, [socket.keyInfo.clientId])
|
await overwriteRemoteListData(socket, remoteListData, key, [socket.keyInfo.clientId])
|
||||||
}
|
}
|
||||||
key ??= await getCurrentListInfoKey()
|
key ??= await getCurrentListInfoKey()
|
||||||
updateDeviceSnapshotKey(socket.keyInfo, key)
|
updateDeviceSnapshotKey(socket.keyInfo, key)
|
||||||
|
@ -399,7 +361,7 @@ const handleMergeListDataFromSnapshot = async(socket: LX.Sync.Server.Socket, sna
|
||||||
newListData.userList = newUserList
|
newListData.userList = newUserList
|
||||||
const key = await handleSetLocalListData(newListData)
|
const key = await handleSetLocalListData(newListData)
|
||||||
await setRemotelList(socket, newListData, key)
|
await setRemotelList(socket, newListData, key)
|
||||||
await overwriteRemoteListData(newListData, key, [socket.keyInfo.clientId])
|
await overwriteRemoteListData(socket, newListData, key, [socket.keyInfo.clientId])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -416,14 +378,41 @@ const syncList = async(socket: LX.Sync.Server.Socket) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default async(_wss: LX.Sync.Server.SocketServer, socket: LX.Sync.Server.Socket) => {
|
// export default async(_wss: LX.Sync.Server.SocketServer, socket: LX.Sync.Server.Socket) => {
|
||||||
if (!wss) {
|
// if (!wss) {
|
||||||
wss = _wss
|
// wss = _wss
|
||||||
_wss.addListener('close', () => {
|
// _wss.addListener('close', () => {
|
||||||
wss = null
|
// wss = null
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// let disconnected = false
|
||||||
|
// socket.onClose(() => {
|
||||||
|
// disconnected = true
|
||||||
|
// if (syncingId == socket.keyInfo.clientId) syncingId = null
|
||||||
|
// })
|
||||||
|
|
||||||
|
// while (true) {
|
||||||
|
// if (disconnected) throw new Error('disconnected')
|
||||||
|
// if (!syncingId) break
|
||||||
|
// await wait()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// syncingId = socket.keyInfo.clientId
|
||||||
|
// await syncList(socket).then(async() => {
|
||||||
|
// // if (newListData) registerUpdateSnapshotTask(socket, { ...newListData })
|
||||||
|
// return finishedSync(socket)
|
||||||
|
// }).finally(() => {
|
||||||
|
// syncingId = null
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const removeSnapshot = async(keyInfo: LX.Sync.KeyInfo) => {
|
||||||
|
// const filePath = getSnapshotFilePath(keyInfo)
|
||||||
|
// await fsPromises.unlink(filePath)
|
||||||
|
// }
|
||||||
|
|
||||||
|
export default async(socket: LX.Sync.Server.Socket) => {
|
||||||
let disconnected = false
|
let disconnected = false
|
||||||
socket.onClose(() => {
|
socket.onClose(() => {
|
||||||
disconnected = true
|
disconnected = true
|
||||||
|
@ -438,14 +427,8 @@ export default async(_wss: LX.Sync.Server.SocketServer, socket: LX.Sync.Server.S
|
||||||
|
|
||||||
syncingId = socket.keyInfo.clientId
|
syncingId = socket.keyInfo.clientId
|
||||||
await syncList(socket).then(async() => {
|
await syncList(socket).then(async() => {
|
||||||
// if (newListData) registerUpdateSnapshotTask(socket, { ...newListData })
|
|
||||||
return finishedSync(socket)
|
return finishedSync(socket)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
syncingId = null
|
syncingId = null
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// const removeSnapshot = async(keyInfo: LX.Sync.KeyInfo) => {
|
|
||||||
// const filePath = getSnapshotFilePath(keyInfo)
|
|
||||||
// await fsPromises.unlink(filePath)
|
|
||||||
// }
|
|
|
@ -1,15 +1,15 @@
|
||||||
import http, { type IncomingMessage } from 'node:http'
|
import http, { type IncomingMessage } from 'node:http'
|
||||||
import url from 'node:url'
|
import url from 'node:url'
|
||||||
import { WebSocketServer } from 'ws'
|
import { WebSocketServer } from 'ws'
|
||||||
import * as modules from './modules'
|
import { modules, callObj } from './modules'
|
||||||
import { authCode, authConnect } from './auth'
|
import { authCode, authConnect } from './auth'
|
||||||
import syncList from './syncList'
|
|
||||||
import log from '../log'
|
import log from '../log'
|
||||||
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@common/constants'
|
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@common/constants'
|
||||||
import { decryptMsg, encryptMsg, generateCode as handleGenerateCode } from './utils'
|
import { decryptMsg, encryptMsg, generateCode as handleGenerateCode } from './utils'
|
||||||
import { getAddress } from '../utils'
|
import { getAddress } from '../utils'
|
||||||
import { sendServerStatus } from '@main/modules/winMain/index'
|
import { sendServerStatus } from '@main/modules/winMain/index'
|
||||||
import { getClientKeyInfo, getServerId, saveClientKeyInfo } from '../data'
|
import { getClientKeyInfo, getServerId, saveClientKeyInfo } from '../data'
|
||||||
|
import { createMsg2call } from 'message2call'
|
||||||
|
|
||||||
|
|
||||||
let status: LX.Sync.ServerStatus = {
|
let status: LX.Sync.ServerStatus = {
|
||||||
|
@ -40,6 +40,24 @@ const codeTools: {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const syncData = async(socket: LX.Sync.Server.Socket) => {
|
||||||
|
for (const module of Object.values(modules)) {
|
||||||
|
await module.sync(socket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const registerLocalSyncEvent = async(wss: LX.Sync.Server.SocketServer) => {
|
||||||
|
for (const module of Object.values(modules)) {
|
||||||
|
module.registerEvent(wss)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const unregisterLocalSyncEvent = () => {
|
||||||
|
for (const module of Object.values(modules)) {
|
||||||
|
module.unregisterEvent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const checkDuplicateClient = (newSocket: LX.Sync.Server.Socket) => {
|
const checkDuplicateClient = (newSocket: LX.Sync.Server.Socket) => {
|
||||||
for (const client of [...wss!.clients]) {
|
for (const client of [...wss!.clients]) {
|
||||||
if (client === newSocket || client.keyInfo.clientId != newSocket.keyInfo.clientId) continue
|
if (client === newSocket || client.keyInfo.clientId != newSocket.keyInfo.clientId) continue
|
||||||
|
@ -65,7 +83,7 @@ const handleConnection = async(socket: LX.Sync.Server.Socket, request: IncomingM
|
||||||
checkDuplicateClient(socket)
|
checkDuplicateClient(socket)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await syncList(wss as LX.Sync.Server.SocketServer, socket)
|
await syncData(socket)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// console.log(err)
|
// console.log(err)
|
||||||
log.warn(err)
|
log.warn(err)
|
||||||
|
@ -83,9 +101,6 @@ const handleConnection = async(socket: LX.Sync.Server.Socket, request: IncomingM
|
||||||
// console.log('connection', keyInfo.deviceName)
|
// console.log('connection', keyInfo.deviceName)
|
||||||
log.info('connection', keyInfo.deviceName)
|
log.info('connection', keyInfo.deviceName)
|
||||||
// console.log(socket.handshake.query)
|
// console.log(socket.handshake.query)
|
||||||
for (const module of Object.values(modules)) {
|
|
||||||
module.registerListHandler(wss as LX.Sync.Server.SocketServer, socket)
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.isReady = true
|
socket.isReady = true
|
||||||
}
|
}
|
||||||
|
@ -93,9 +108,6 @@ const handleConnection = async(socket: LX.Sync.Server.Socket, request: IncomingM
|
||||||
const handleUnconnection = () => {
|
const handleUnconnection = () => {
|
||||||
console.log('unconnection')
|
console.log('unconnection')
|
||||||
// console.log(socket.handshake.query)
|
// console.log(socket.handshake.query)
|
||||||
for (const module of Object.values(modules)) {
|
|
||||||
module.unregisterListHandler()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const authConnection = (req: http.IncomingMessage, callback: (err: string | null | undefined, success: boolean) => void) => {
|
const authConnection = (req: http.IncomingMessage, callback: (err: string | null | undefined, success: boolean) => void) => {
|
||||||
|
@ -157,52 +169,73 @@ const handleStartServer = async(port = 9527, ip = '0.0.0.0') => await new Promis
|
||||||
|
|
||||||
// const events = new Map<keyof ActionsType, Array<(err: Error | null, data: LX.Sync.ActionSyncType[keyof LX.Sync.ActionSyncType]) => void>>()
|
// const events = new Map<keyof ActionsType, Array<(err: Error | null, data: LX.Sync.ActionSyncType[keyof LX.Sync.ActionSyncType]) => void>>()
|
||||||
// const events = new Map<keyof LX.Sync.ActionSyncType, Array<(err: Error | null, data: LX.Sync.ActionSyncType[keyof LX.Sync.ActionSyncType]) => void>>()
|
// const events = new Map<keyof LX.Sync.ActionSyncType, Array<(err: Error | null, data: LX.Sync.ActionSyncType[keyof LX.Sync.ActionSyncType]) => void>>()
|
||||||
let events: Partial<{ [K in keyof LX.Sync.ActionSyncType]: Array<(data: LX.Sync.ActionSyncType[K]) => void> }> = {}
|
// let events: Partial<{ [K in keyof LX.Sync.ActionSyncType]: Array<(data: LX.Sync.ActionSyncType[K]) => void> }> = {}
|
||||||
let closeEvents: Array<(err: Error) => (void | Promise<void>)> = []
|
let closeEvents: Array<(err: Error) => (void | Promise<void>)> = []
|
||||||
|
const msg2call = createMsg2call<LX.Sync.ClientActions>({
|
||||||
|
funcsObj: callObj,
|
||||||
|
timeout: 120 * 1000,
|
||||||
|
sendMessage(data) {
|
||||||
|
void encryptMsg(socket.keyInfo, JSON.stringify(data)).then((data) => {
|
||||||
|
// console.log('sendData', eventName)
|
||||||
|
socket.send(data)
|
||||||
|
}).catch(err => {
|
||||||
|
log.error('encrypt message error:', err)
|
||||||
|
log.error(err.message)
|
||||||
|
socket.close(SYNC_CLOSE_CODE.failed)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onCallBeforeParams(rawArgs) {
|
||||||
|
return [socket, ...rawArgs]
|
||||||
|
},
|
||||||
|
onError(error, path, groupName) {
|
||||||
|
const name = groupName ?? ''
|
||||||
|
log.error(`sync call ${name} ${path.join('.')} error:`, error)
|
||||||
|
if (groupName == null) return
|
||||||
|
socket.close(SYNC_CLOSE_CODE.failed)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
socket.remote = msg2call.remote
|
||||||
|
socket.remoteSyncList = msg2call.createSyncRemote('list')
|
||||||
socket.addEventListener('message', ({ data }) => {
|
socket.addEventListener('message', ({ data }) => {
|
||||||
if (typeof data === 'string') {
|
if (typeof data != 'string') return
|
||||||
let syncData: LX.Sync.ActionSync
|
void decryptMsg(socket.keyInfo, data).then((data) => {
|
||||||
|
let syncData: any
|
||||||
try {
|
try {
|
||||||
syncData = JSON.parse(decryptMsg(socket.keyInfo, data))
|
syncData = JSON.parse(data)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.error('parse message error:', err)
|
log.error('parse message error:', err)
|
||||||
socket.close(SYNC_CLOSE_CODE.failed)
|
socket.close(SYNC_CLOSE_CODE.failed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const handlers = events[syncData.action]
|
msg2call.onMessage(syncData)
|
||||||
if (handlers) {
|
}).catch(err => {
|
||||||
// @ts-expect-error
|
log.error('decrypt message error:', err)
|
||||||
for (const handler of handlers) handler(syncData.data)
|
log.error(err.message)
|
||||||
}
|
socket.close(SYNC_CLOSE_CODE.failed)
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
socket.addEventListener('close', () => {
|
socket.addEventListener('close', () => {
|
||||||
|
if (!socket.isReady) {
|
||||||
|
const queryData = url.parse(request.url as string, true).query as Record<string, string>
|
||||||
|
log.info('deconnection', queryData.i)
|
||||||
|
return
|
||||||
|
}
|
||||||
const err = new Error('closed')
|
const err = new Error('closed')
|
||||||
for (const handler of closeEvents) void handler(err)
|
for (const handler of closeEvents) void handler(err)
|
||||||
events = {}
|
// events = {}
|
||||||
closeEvents = []
|
closeEvents = []
|
||||||
if (!status.devices.length) handleUnconnection()
|
if (!status.devices.length) handleUnconnection()
|
||||||
log.info('deconnection', socket.keyInfo.deviceName)
|
log.info('deconnection', socket.keyInfo.deviceName)
|
||||||
})
|
})
|
||||||
socket.onRemoteEvent = function(eventName, handler) {
|
|
||||||
let eventArr = events[eventName]
|
|
||||||
if (!eventArr) events[eventName] = eventArr = []
|
|
||||||
// let eventArr = events.get(eventName)
|
|
||||||
// if (!eventArr) events.set(eventName, eventArr = [])
|
|
||||||
eventArr.push(handler)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
eventArr!.splice(eventArr!.indexOf(handler), 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
socket.onClose = function(handler: typeof closeEvents[number]) {
|
socket.onClose = function(handler: typeof closeEvents[number]) {
|
||||||
closeEvents.push(handler)
|
closeEvents.push(handler)
|
||||||
return () => {
|
return () => {
|
||||||
closeEvents.splice(closeEvents.indexOf(handler), 1)
|
closeEvents.splice(closeEvents.indexOf(handler), 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
socket.sendData = function(eventName, data, callback) {
|
socket.broadcast = function(handler) {
|
||||||
socket.send(encryptMsg(socket.keyInfo, JSON.stringify({ action: eventName, data })), callback)
|
if (!wss) return
|
||||||
|
for (const client of wss.clients) handler(client)
|
||||||
}
|
}
|
||||||
void handleConnection(socket, request)
|
void handleConnection(socket, request)
|
||||||
})
|
})
|
||||||
|
@ -256,6 +289,7 @@ const handleStartServer = async(port = 9527, ip = '0.0.0.0') => await new Promis
|
||||||
const bind = typeof addr == 'string' ? `pipe ${addr}` : `port ${addr.port}`
|
const bind = typeof addr == 'string' ? `pipe ${addr}` : `port ${addr.port}`
|
||||||
log.info(`Listening on ${ip} ${bind}`)
|
log.info(`Listening on ${ip} ${bind}`)
|
||||||
resolve(null)
|
resolve(null)
|
||||||
|
void registerLocalSyncEvent(wss as LX.Sync.Server.SocketServer)
|
||||||
})
|
})
|
||||||
|
|
||||||
httpServer.listen(port, ip)
|
httpServer.listen(port, ip)
|
||||||
|
@ -264,6 +298,7 @@ const handleStartServer = async(port = 9527, ip = '0.0.0.0') => await new Promis
|
||||||
const handleStopServer = async() => new Promise<void>((resolve, reject) => {
|
const handleStopServer = async() => new Promise<void>((resolve, reject) => {
|
||||||
if (!wss) return
|
if (!wss) return
|
||||||
for (const client of wss.clients) client.close(SYNC_CLOSE_CODE.normal)
|
for (const client of wss.clients) client.close(SYNC_CLOSE_CODE.normal)
|
||||||
|
unregisterLocalSyncEvent()
|
||||||
wss.close()
|
wss.close()
|
||||||
wss = null
|
wss = null
|
||||||
httpServer.close((err) => {
|
httpServer.close((err) => {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
saveSnapshotInfo,
|
saveSnapshotInfo,
|
||||||
type SnapshotInfo,
|
type SnapshotInfo,
|
||||||
} from '../data'
|
} from '../data'
|
||||||
import { getLocalListData } from '../utils'
|
import { decodeData, encodeData, getLocalListData } from '../utils'
|
||||||
|
|
||||||
export const generateCode = (): string => {
|
export const generateCode = (): string => {
|
||||||
return Math.random().toString().substring(2, 8)
|
return Math.random().toString().substring(2, 8)
|
||||||
|
@ -16,14 +16,18 @@ export const getIP = (request: http.IncomingMessage) => {
|
||||||
return request.socket.remoteAddress
|
return request.socket.remoteAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
export const encryptMsg = (keyInfo: LX.Sync.ServerKeyInfo, msg: string): string => {
|
export const encryptMsg = async(keyInfo: LX.Sync.ServerKeyInfo | null, msg: string): Promise<string> => {
|
||||||
return msg
|
return encodeData(msg)
|
||||||
|
// console.log('enmsg raw: ', msg.length, 'en: ', len.length)
|
||||||
|
// return len
|
||||||
// if (!keyInfo) return ''
|
// if (!keyInfo) return ''
|
||||||
// return aesEncrypt(msg, keyInfo.key, keyInfo.iv)
|
// return aesEncrypt(msg, keyInfo.key, keyInfo.iv)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const decryptMsg = (keyInfo: LX.Sync.ServerKeyInfo, enMsg: string): string => {
|
export const decryptMsg = async(keyInfo: LX.Sync.ServerKeyInfo | null, enMsg: string): Promise<string> => {
|
||||||
return enMsg
|
return decodeData(enMsg)
|
||||||
|
// console.log('decmsg raw: ', len.length, 'en: ', enMsg.length)
|
||||||
|
// return len
|
||||||
// if (!keyInfo) return ''
|
// if (!keyInfo) return ''
|
||||||
// let msg = ''
|
// let msg = ''
|
||||||
// try {
|
// try {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { createCipheriv, createDecipheriv, publicEncrypt, privateDecrypt, constants } from 'node:crypto'
|
import { createCipheriv, createDecipheriv, publicEncrypt, privateDecrypt, constants } from 'node:crypto'
|
||||||
import os, { networkInterfaces } from 'node:os'
|
import os, { networkInterfaces } from 'node:os'
|
||||||
|
import zlib from 'node:zlib'
|
||||||
import cp from 'node:child_process'
|
import cp from 'node:child_process'
|
||||||
import { LIST_IDS } from '@common/constants'
|
import { LIST_IDS } from '@common/constants'
|
||||||
|
|
||||||
|
@ -40,6 +41,38 @@ export const getComputerName = () => {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gzip = async(data: string) => new Promise<string>((resolve, reject) => {
|
||||||
|
zlib.gzip(data, (err, buf) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resolve(buf.toString('base64'))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const unGzip = async(data: string) => new Promise<string>((resolve, reject) => {
|
||||||
|
zlib.gunzip(Buffer.from(data, 'base64'), (err, buf) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resolve(buf.toString())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export const encodeData = async(data: string) => {
|
||||||
|
return data.length > 1024
|
||||||
|
? 'cg_' + await gzip(data)
|
||||||
|
: data
|
||||||
|
}
|
||||||
|
|
||||||
|
export const decodeData = async(enData: string) => {
|
||||||
|
return enData.substring(0, 3) == 'cg_'
|
||||||
|
? await unGzip(enData.replace('cg_', ''))
|
||||||
|
: enData
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const aesEncrypt = (text: string, key: string) => {
|
export const aesEncrypt = (text: string, key: string) => {
|
||||||
const cipher = createCipheriv('aes-128-ecb', Buffer.from(key, 'base64'), '')
|
const cipher = createCipheriv('aes-128-ecb', Buffer.from(key, 'base64'), '')
|
||||||
return Buffer.concat([cipher.update(Buffer.from(text)), cipher.final()]).toString('base64')
|
return Buffer.concat([cipher.update(Buffer.from(text)), cipher.final()]).toString('base64')
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
|
||||||
import { startServer, stopServer, getServerStatus, generateCode, connectServer, disconnectServer, getClientStatus } from '@main/modules/sync'
|
import { startServer, stopServer, getServerStatus, generateCode, connectServer, disconnectServer, getClientStatus } from '@main/modules/sync'
|
||||||
import { sendEvent } from '../main'
|
import { sendEvent } from '../main'
|
||||||
|
|
||||||
let selectModeListenr: ((mode: LX.Sync.Mode) => void) | null = null
|
let selectModeListenr: ((mode: LX.Sync.Mode | null) => void) | null = null
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
mainHandle<LX.Sync.SyncServiceActions, any>(WIN_MAIN_RENDERER_EVENT_NAME.sync_action, async({ params: data }) => {
|
mainHandle<LX.Sync.SyncServiceActions, any>(WIN_MAIN_RENDERER_EVENT_NAME.sync_action, async({ params: data }) => {
|
||||||
|
@ -18,7 +18,10 @@ export default () => {
|
||||||
case 'get_client_status': return getClientStatus()
|
case 'get_client_status': return getClientStatus()
|
||||||
case 'generate_code': return generateCode()
|
case 'generate_code': return generateCode()
|
||||||
case 'select_mode':
|
case 'select_mode':
|
||||||
if (selectModeListenr) selectModeListenr(data.data)
|
if (selectModeListenr) {
|
||||||
|
selectModeListenr(data.data)
|
||||||
|
selectModeListenr = null
|
||||||
|
}
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
@ -43,11 +46,12 @@ export const sendServerStatus = (status: LX.Sync.ServerStatus) => {
|
||||||
data: status,
|
data: status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export const sendSelectMode = (deviceName: string, listener: (mode: LX.Sync.Mode) => void) => {
|
export const sendSelectMode = (deviceName: string, listener: (mode: LX.Sync.Mode | null) => void) => {
|
||||||
selectModeListenr = listener
|
selectModeListenr = listener
|
||||||
sendSyncAction({ action: 'select_mode', data: deviceName })
|
sendSyncAction({ action: 'select_mode', data: deviceName })
|
||||||
}
|
}
|
||||||
export const removeSelectModeListener = () => {
|
export const removeSelectModeListener = () => {
|
||||||
|
if (selectModeListenr) selectModeListenr(null)
|
||||||
selectModeListenr = null
|
selectModeListenr = null
|
||||||
}
|
}
|
||||||
export const sendCloseSelectMode = () => {
|
export const sendCloseSelectMode = () => {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import '@common/types/app_setting'
|
||||||
import '@common/types/common'
|
import '@common/types/common'
|
||||||
import '@common/types/user_api'
|
import '@common/types/user_api'
|
||||||
import '@common/types/sync'
|
import '@common/types/sync'
|
||||||
|
import '@common/types/sync_common'
|
||||||
import '@common/types/list'
|
import '@common/types/list'
|
||||||
import '@common/types/download_list'
|
import '@common/types/download_list'
|
||||||
import '@common/types/music'
|
import '@common/types/music'
|
||||||
|
|
|
@ -14,18 +14,8 @@ declare global {
|
||||||
urlInfo: UrlInfo
|
urlInfo: UrlInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemoteEvent: <T extends keyof LX.Sync.ActionSyncSendType>(
|
|
||||||
eventName: T,
|
|
||||||
handler: (data: LX.Sync.ActionSyncSendType[T]) => (void | Promise<void>)
|
|
||||||
) => () => void
|
|
||||||
|
|
||||||
onClose: (handler: (err: Error) => (void | Promise<void>)) => () => void
|
onClose: (handler: (err: Error) => (void | Promise<void>)) => () => void
|
||||||
|
remoteSyncList: LX.Sync.ServerActions
|
||||||
sendData: <T extends keyof LX.Sync.ActionSyncType>(
|
|
||||||
eventName: T,
|
|
||||||
data?: LX.Sync.ActionSyncType[T],
|
|
||||||
callback?: (err?: Error) => void
|
|
||||||
) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UrlInfo {
|
interface UrlInfo {
|
||||||
|
@ -38,22 +28,13 @@ declare global {
|
||||||
namespace Server {
|
namespace Server {
|
||||||
interface Socket extends WS.WebSocket {
|
interface Socket extends WS.WebSocket {
|
||||||
isAlive?: boolean
|
isAlive?: boolean
|
||||||
isMobile: boolean
|
|
||||||
isReady: boolean
|
isReady: boolean
|
||||||
keyInfo: LX.Sync.ServerKeyInfo
|
keyInfo: ServerKeyInfo
|
||||||
|
|
||||||
onRemoteEvent: <T extends keyof LX.Sync.ActionSyncType>(
|
|
||||||
eventName: T,
|
|
||||||
handler: (data: LX.Sync.ActionSyncType[T]) => void
|
|
||||||
) => () => void
|
|
||||||
|
|
||||||
onClose: (handler: (err: Error) => (void | Promise<void>)) => () => void
|
onClose: (handler: (err: Error) => (void | Promise<void>)) => () => void
|
||||||
|
broadcast: (handler: (client: Socket) => void) => void
|
||||||
|
|
||||||
sendData: <T extends keyof LX.Sync.ActionSyncSendType>(
|
remote: LX.Sync.ClientActions
|
||||||
eventName: T,
|
remoteSyncList: LX.Sync.ClientActions
|
||||||
data?: LX.Sync.ActionSyncSendType[T],
|
|
||||||
callback?: (err?: Error) => void
|
|
||||||
) => void
|
|
||||||
}
|
}
|
||||||
type SocketServer = WS.Server<Socket>
|
type SocketServer = WS.Server<Socket>
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue