重构同步服务端功能部分代码,使其更易扩展新功能
parent
70815e276e
commit
0d76c9ccb4
|
@ -31,3 +31,4 @@
|
||||||
### 其他
|
### 其他
|
||||||
|
|
||||||
- 更新 electron 到 v22.3.21
|
- 更新 electron 到 v22.3.21
|
||||||
|
- 重构同步服务端功能部分代码,使其更易扩展新功能
|
||||||
|
|
|
@ -140,5 +140,18 @@ declare namespace LX {
|
||||||
tempList: LX.Music.MusicInfo[]
|
tempList: LX.Music.MusicInfo[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ declare namespace LX {
|
||||||
| SyncAction<'client_status', ClientStatus>
|
| SyncAction<'client_status', ClientStatus>
|
||||||
| SyncAction<'server_status', ServerStatus>
|
| SyncAction<'server_status', ServerStatus>
|
||||||
|
|
||||||
type SyncServiceActions = SyncAction<'select_mode', Mode>
|
type SyncServiceActions = SyncAction<'select_mode', ListSyncMode>
|
||||||
| SyncAction<'get_server_status'>
|
| SyncAction<'get_server_status'>
|
||||||
| SyncAction<'get_client_status'>
|
| SyncAction<'get_client_status'>
|
||||||
| SyncAction<'generate_code'>
|
| SyncAction<'generate_code'>
|
||||||
|
@ -90,14 +90,17 @@ declare namespace LX {
|
||||||
clientId: string
|
clientId: string
|
||||||
key: string
|
key: string
|
||||||
deviceName: string
|
deviceName: string
|
||||||
lastSyncDate?: number
|
lastConnectDate?: number
|
||||||
snapshotKey: string
|
|
||||||
isMobile: boolean
|
isMobile: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ListInfo {
|
||||||
|
lastSyncDate?: number
|
||||||
|
snapshotKey: string
|
||||||
|
}
|
||||||
type ListData = Omit<LX.List.ListDataFull, 'tempList'>
|
type ListData = Omit<LX.List.ListDataFull, 'tempList'>
|
||||||
|
|
||||||
type Mode = 'merge_local_remote'
|
type ListSyncMode = 'merge_local_remote'
|
||||||
| 'merge_remote_local'
|
| 'merge_remote_local'
|
||||||
| 'overwrite_local_remote'
|
| 'overwrite_local_remote'
|
||||||
| 'overwrite_remote_local'
|
| 'overwrite_remote_local'
|
||||||
|
|
|
@ -1,26 +1,13 @@
|
||||||
declare namespace LX {
|
declare namespace LX {
|
||||||
namespace Sync {
|
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<{
|
type ServerActions = WarpPromiseRecord<{
|
||||||
onListSyncAction: (action: LX.Sync.ActionList) => void
|
onListSyncAction: (action: LX.List.ActionList) => void
|
||||||
}>
|
}>
|
||||||
|
|
||||||
type ClientActions = WarpPromiseRecord<{
|
type ClientActions = WarpPromiseRecord<{
|
||||||
onListSyncAction: (action: LX.Sync.ActionList) => void
|
onListSyncAction: (action: LX.List.ActionList) => void
|
||||||
list_sync_get_md5: () => string
|
list_sync_get_md5: () => string
|
||||||
list_sync_get_sync_mode: () => Mode
|
list_sync_get_sync_mode: () => ListSyncMode
|
||||||
list_sync_get_list_data: () => ListData
|
list_sync_get_list_data: () => ListData
|
||||||
list_sync_set_list_data: (data: ListData) => void
|
list_sync_set_list_data: (data: ListData) => void
|
||||||
list_sync_finished: () => void
|
list_sync_finished: () => void
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { request, generateRsaKey } from './utils'
|
import { request, generateRsaKey } from './utils'
|
||||||
import { getSyncAuthKey, setSyncAuthKey } from '../data'
|
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'
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { File } from '../constants'
|
||||||
|
|
||||||
|
|
||||||
|
let syncAuthKeys: Record<string, LX.Sync.ClientKeyInfo>
|
||||||
|
|
||||||
|
|
||||||
|
const saveSyncAuthKeys = async() => {
|
||||||
|
const syncAuthKeysFilePath = path.join(global.lxDataPath, File.clientDataPath, File.syncAuthKeysJSON)
|
||||||
|
return fs.promises.writeFile(syncAuthKeysFilePath, JSON.stringify(syncAuthKeys), 'utf8')
|
||||||
|
}
|
||||||
|
|
||||||
|
const exists = async(path: string) => fs.promises.stat(path).then(() => true).catch(() => false)
|
||||||
|
export const initClientInfo = async() => {
|
||||||
|
if (syncAuthKeys != null) return
|
||||||
|
const syncAuthKeysFilePath = path.join(global.lxDataPath, File.clientDataPath, File.syncAuthKeysJSON)
|
||||||
|
if (await fs.promises.stat(syncAuthKeysFilePath).then(() => true).catch(() => false)) {
|
||||||
|
syncAuthKeys = JSON.parse((await fs.promises.readFile(syncAuthKeysFilePath)).toString())
|
||||||
|
} else {
|
||||||
|
syncAuthKeys = {}
|
||||||
|
const syncDataPath = path.join(global.lxDataPath, File.clientDataPath)
|
||||||
|
if (!await exists(syncDataPath)) {
|
||||||
|
await fs.promises.mkdir(syncDataPath, { recursive: true })
|
||||||
|
}
|
||||||
|
void saveSyncAuthKeys()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSyncAuthKey = async(serverId: string) => {
|
||||||
|
await initClientInfo()
|
||||||
|
return syncAuthKeys[serverId] ?? null
|
||||||
|
}
|
||||||
|
export const setSyncAuthKey = async(serverId: string, info: LX.Sync.ClientKeyInfo) => {
|
||||||
|
await initClientInfo()
|
||||||
|
syncAuthKeys[serverId] = info
|
||||||
|
void saveSyncAuthKeys()
|
||||||
|
}
|
||||||
|
|
||||||
|
// let syncHost: string
|
||||||
|
// export const getSyncHost = async() => {
|
||||||
|
// if (syncHost === undefined) {
|
||||||
|
// const store = getStore(STORE_NAMES.SYNC)
|
||||||
|
// syncHost = (store.get('syncHost') as typeof syncHost | null) ?? ''
|
||||||
|
// }
|
||||||
|
// return syncHost
|
||||||
|
// }
|
||||||
|
// export const setSyncHost = async(host: string) => {
|
||||||
|
// // let hostInfo = await getData(syncHostPrefix) || {}
|
||||||
|
// // hostInfo.host = host
|
||||||
|
// // hostInfo.port = port
|
||||||
|
// syncHost = host
|
||||||
|
// const store = getStore(STORE_NAMES.SYNC)
|
||||||
|
// store.set('syncHost', syncHost)
|
||||||
|
// }
|
||||||
|
// let syncHostHistory: string[]
|
||||||
|
// export const getSyncHostHistory = async() => {
|
||||||
|
// if (syncHostHistory === undefined) {
|
||||||
|
// const store = getStore(STORE_NAMES.SYNC)
|
||||||
|
// syncHostHistory = (store.get('syncHostHistory') as string[]) ?? []
|
||||||
|
// }
|
||||||
|
// return syncHostHistory
|
||||||
|
// }
|
||||||
|
// export const addSyncHostHistory = async(host: string) => {
|
||||||
|
// let syncHostHistory = await getSyncHostHistory()
|
||||||
|
// if (syncHostHistory.some(h => h == host)) return
|
||||||
|
// syncHostHistory.unshift(host)
|
||||||
|
// if (syncHostHistory.length > 20) syncHostHistory = syncHostHistory.slice(0, 20) // 最多存储20个
|
||||||
|
// const store = getStore(STORE_NAMES.SYNC)
|
||||||
|
// store.set('syncHostHistory', syncHostHistory)
|
||||||
|
// }
|
||||||
|
// export const removeSyncHostHistory = async(index: number) => {
|
||||||
|
// syncHostHistory.splice(index, 1)
|
||||||
|
// const store = getStore(STORE_NAMES.SYNC)
|
||||||
|
// store.set('syncHostHistory', syncHostHistory)
|
||||||
|
// }
|
|
@ -4,6 +4,7 @@ import { connect as socketConnect, disconnect as socketDisconnect, sendSyncStatu
|
||||||
import { SYNC_CODE } from '@common/constants'
|
import { SYNC_CODE } from '@common/constants'
|
||||||
import log from '../log'
|
import log from '../log'
|
||||||
import { parseUrl } from './utils'
|
import { parseUrl } from './utils'
|
||||||
|
import migrateData from '../migrate'
|
||||||
|
|
||||||
let connectId = 0
|
let connectId = 0
|
||||||
|
|
||||||
|
@ -29,6 +30,8 @@ const connectServer = async(host: string, authCode?: string) => {
|
||||||
message: SYNC_CODE.connecting,
|
message: SYNC_CODE.connecting,
|
||||||
})
|
})
|
||||||
const id = connectId
|
const id = connectId
|
||||||
|
await migrateData(global.lxDataPath)
|
||||||
|
|
||||||
return handleConnect(host, authCode).catch(async err => {
|
return handleConnect(host, authCode).catch(async err => {
|
||||||
if (id != connectId) return
|
if (id != connectId) return
|
||||||
sendSyncStatus({
|
sendSyncStatus({
|
||||||
|
|
|
@ -21,7 +21,7 @@ export const list_sync_get_md5 = async(socket: LX.Sync.Client.Socket) => {
|
||||||
return toMD5(JSON.stringify(await getLocalListData()))
|
return toMD5(JSON.stringify(await getLocalListData()))
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSyncMode = async(socket: LX.Sync.Client.Socket): Promise<LX.Sync.Mode> => new Promise((resolve, reject) => {
|
const getSyncMode = async(socket: LX.Sync.Client.Socket): Promise<LX.Sync.ListSyncMode> => new Promise((resolve, reject) => {
|
||||||
const handleDisconnect = (err: Error) => {
|
const handleDisconnect = (err: Error) => {
|
||||||
sendCloseSelectMode()
|
sendCloseSelectMode()
|
||||||
removeSelectModeListener()
|
removeSelectModeListener()
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
export const ENV_PARAMS = [
|
||||||
|
'PORT',
|
||||||
|
'BIND_IP',
|
||||||
|
'CONFIG_PATH',
|
||||||
|
'LOG_PATH',
|
||||||
|
'DATA_PATH',
|
||||||
|
'PROXY_HEADER',
|
||||||
|
'MAX_SNAPSHOT_NUM',
|
||||||
|
'LIST_ADD_MUSIC_LOCATION_TYPE',
|
||||||
|
'LX_USER_',
|
||||||
|
] as const
|
||||||
|
|
||||||
|
|
||||||
|
export const LIST_IDS = {
|
||||||
|
DEFAULT: 'default',
|
||||||
|
LOVE: 'love',
|
||||||
|
TEMP: 'temp',
|
||||||
|
DOWNLOAD: 'download',
|
||||||
|
PLAY_LATER: null,
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const SYNC_CODE = {
|
||||||
|
helloMsg: 'Hello~::^-^::~v4~',
|
||||||
|
idPrefix: 'OjppZDo6',
|
||||||
|
authMsg: 'lx-music auth::',
|
||||||
|
msgAuthFailed: 'Auth failed',
|
||||||
|
msgBlockedIp: 'Blocked IP',
|
||||||
|
msgConnect: 'lx-music connect',
|
||||||
|
|
||||||
|
|
||||||
|
authFailed: 'Auth failed',
|
||||||
|
missingAuthCode: 'Missing auth code',
|
||||||
|
getServiceIdFailed: 'Get service id failed',
|
||||||
|
connectServiceFailed: 'Connect service failed',
|
||||||
|
connecting: 'Connecting...',
|
||||||
|
unknownServiceAddress: 'Unknown service address',
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const SYNC_CLOSE_CODE = {
|
||||||
|
normal: 1000,
|
||||||
|
failed: 4100,
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const TRANS_MODE: Readonly<Record<LX.Sync.ListSyncMode, LX.Sync.ListSyncMode>> = {
|
||||||
|
merge_local_remote: 'merge_remote_local',
|
||||||
|
merge_remote_local: 'merge_local_remote',
|
||||||
|
overwrite_local_remote: 'overwrite_remote_local',
|
||||||
|
overwrite_remote_local: 'overwrite_local_remote',
|
||||||
|
overwrite_local_remote_full: 'overwrite_remote_local_full',
|
||||||
|
overwrite_remote_local_full: 'overwrite_local_remote_full',
|
||||||
|
cancel: 'cancel',
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const File = {
|
||||||
|
serverDataPath: 'sync/server',
|
||||||
|
clientDataPath: 'sync/client',
|
||||||
|
|
||||||
|
serverInfoJSON: 'serverInfo.json',
|
||||||
|
userDir: 'users',
|
||||||
|
userDevicesJSON: 'devices.json',
|
||||||
|
listDir: 'list',
|
||||||
|
listSnapshotDir: 'snapshot',
|
||||||
|
listSnapshotInfoJSON: 'snapshotInfo.json',
|
||||||
|
|
||||||
|
syncAuthKeysJSON: 'syncAuthKey.json',
|
||||||
|
} as const
|
|
@ -1,185 +0,0 @@
|
||||||
import { randomBytes } from 'node:crypto'
|
|
||||||
import { STORE_NAMES } from '@common/constants'
|
|
||||||
import getStore from '@main/utils/store'
|
|
||||||
import { throttle } from '@common/utils/common'
|
|
||||||
import path from 'node:path'
|
|
||||||
import fs from 'node:fs'
|
|
||||||
import log from './log'
|
|
||||||
|
|
||||||
|
|
||||||
export const getSyncAuthKey = async(serverId: string) => {
|
|
||||||
const store = getStore(STORE_NAMES.SYNC)
|
|
||||||
const keys = store.get('syncAuthKey') as Record<string, LX.Sync.ClientKeyInfo> | null
|
|
||||||
if (!keys) return null
|
|
||||||
return keys[serverId] ?? null
|
|
||||||
}
|
|
||||||
export const setSyncAuthKey = async(serverId: string, info: LX.Sync.ClientKeyInfo) => {
|
|
||||||
const store = getStore(STORE_NAMES.SYNC)
|
|
||||||
let keys: Record<string, LX.Sync.ClientKeyInfo> = (store.get('syncAuthKey') as Record<string, LX.Sync.ClientKeyInfo> | null) ?? {}
|
|
||||||
keys[serverId] = info
|
|
||||||
store.set('syncAuthKey', keys)
|
|
||||||
}
|
|
||||||
|
|
||||||
let syncHost: string
|
|
||||||
export const getSyncHost = async() => {
|
|
||||||
if (syncHost === undefined) {
|
|
||||||
const store = getStore(STORE_NAMES.SYNC)
|
|
||||||
syncHost = (store.get('syncHost') as typeof syncHost | null) ?? ''
|
|
||||||
}
|
|
||||||
return syncHost
|
|
||||||
}
|
|
||||||
export const setSyncHost = async(host: string) => {
|
|
||||||
// let hostInfo = await getData(syncHostPrefix) || {}
|
|
||||||
// hostInfo.host = host
|
|
||||||
// hostInfo.port = port
|
|
||||||
syncHost = host
|
|
||||||
const store = getStore(STORE_NAMES.SYNC)
|
|
||||||
store.set('syncHost', syncHost)
|
|
||||||
}
|
|
||||||
let syncHostHistory: string[]
|
|
||||||
export const getSyncHostHistory = async() => {
|
|
||||||
if (syncHostHistory === undefined) {
|
|
||||||
const store = getStore(STORE_NAMES.SYNC)
|
|
||||||
syncHostHistory = (store.get('syncHostHistory') as string[]) ?? []
|
|
||||||
}
|
|
||||||
return syncHostHistory
|
|
||||||
}
|
|
||||||
export const addSyncHostHistory = async(host: string) => {
|
|
||||||
let syncHostHistory = await getSyncHostHistory()
|
|
||||||
if (syncHostHistory.some(h => h == host)) return
|
|
||||||
syncHostHistory.unshift(host)
|
|
||||||
if (syncHostHistory.length > 20) syncHostHistory = syncHostHistory.slice(0, 20) // 最多存储20个
|
|
||||||
const store = getStore(STORE_NAMES.SYNC)
|
|
||||||
store.set('syncHostHistory', syncHostHistory)
|
|
||||||
}
|
|
||||||
export const removeSyncHostHistory = async(index: number) => {
|
|
||||||
syncHostHistory.splice(index, 1)
|
|
||||||
const store = getStore(STORE_NAMES.SYNC)
|
|
||||||
store.set('syncHostHistory', syncHostHistory)
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SnapshotInfo {
|
|
||||||
latest: string | null
|
|
||||||
time: number
|
|
||||||
list: string[]
|
|
||||||
}
|
|
||||||
interface DevicesInfo {
|
|
||||||
serverId: string
|
|
||||||
clients: Record<string, LX.Sync.ServerKeyInfo>
|
|
||||||
snapshotInfo: SnapshotInfo
|
|
||||||
}
|
|
||||||
// const devicesFilePath = path.join(global.lx.dataPath, 'devices.json')
|
|
||||||
const devicesInfo: DevicesInfo = { serverId: '', clients: {}, snapshotInfo: { latest: null, time: 0, list: [] } }
|
|
||||||
let deviceKeys: string[] = []
|
|
||||||
const saveDevicesInfoThrottle = throttle(() => {
|
|
||||||
const store = getStore(STORE_NAMES.SYNC)
|
|
||||||
store.set('clients', devicesInfo.clients)
|
|
||||||
})
|
|
||||||
|
|
||||||
const initDeviceInfo = () => {
|
|
||||||
const store = getStore(STORE_NAMES.SYNC)
|
|
||||||
const serverId = store.get('serverId') as string | undefined
|
|
||||||
if (serverId) devicesInfo.serverId = serverId
|
|
||||||
else {
|
|
||||||
devicesInfo.serverId = randomBytes(4 * 4).toString('base64')
|
|
||||||
const store = getStore(STORE_NAMES.SYNC)
|
|
||||||
store.set('serverId', devicesInfo.serverId)
|
|
||||||
}
|
|
||||||
const devices = store.get('clients') as DevicesInfo['clients'] | undefined
|
|
||||||
if (devices) devicesInfo.clients = devices
|
|
||||||
deviceKeys = Object.values(devicesInfo.clients).map(device => device.snapshotKey).filter(k => k)
|
|
||||||
const snapshotInfo = store.get('snapshotInfo') as DevicesInfo['snapshotInfo'] | undefined
|
|
||||||
if (snapshotInfo) devicesInfo.snapshotInfo = snapshotInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createClientKeyInfo = (deviceName: string, isMobile: boolean): LX.Sync.ServerKeyInfo => {
|
|
||||||
const keyInfo: LX.Sync.ServerKeyInfo = {
|
|
||||||
clientId: randomBytes(4 * 4).toString('base64'),
|
|
||||||
key: randomBytes(16).toString('base64'),
|
|
||||||
deviceName,
|
|
||||||
isMobile,
|
|
||||||
snapshotKey: '',
|
|
||||||
lastSyncDate: 0,
|
|
||||||
}
|
|
||||||
saveClientKeyInfo(keyInfo)
|
|
||||||
return keyInfo
|
|
||||||
}
|
|
||||||
export const saveClientKeyInfo = (keyInfo: LX.Sync.ServerKeyInfo) => {
|
|
||||||
if (devicesInfo.clients[keyInfo.clientId] == null && Object.keys(devicesInfo.clients).length > 101) throw new Error('max keys')
|
|
||||||
devicesInfo.clients[keyInfo.clientId] = keyInfo
|
|
||||||
saveDevicesInfoThrottle()
|
|
||||||
}
|
|
||||||
export const getClientKeyInfo = (clientId?: string): LX.Sync.ServerKeyInfo | null => {
|
|
||||||
if (!clientId) return null
|
|
||||||
if (!devicesInfo.serverId) initDeviceInfo()
|
|
||||||
return devicesInfo.clients[clientId] ?? null
|
|
||||||
}
|
|
||||||
export const getServerId = (): string => {
|
|
||||||
if (!devicesInfo.serverId) initDeviceInfo()
|
|
||||||
return devicesInfo.serverId
|
|
||||||
}
|
|
||||||
export const isIncluedsDevice = (name: string) => {
|
|
||||||
return deviceKeys.includes(name)
|
|
||||||
}
|
|
||||||
export const clearOldSnapshot = async() => {
|
|
||||||
if (!devicesInfo.snapshotInfo) return
|
|
||||||
const snapshotList = devicesInfo.snapshotInfo.list.filter(name => !isIncluedsDevice(name))
|
|
||||||
let requiredSave = snapshotList.length > global.lx.appSetting['sync.server.maxSsnapshotNum']
|
|
||||||
while (snapshotList.length > global.lx.appSetting['sync.server.maxSsnapshotNum']) {
|
|
||||||
const name = snapshotList.pop()
|
|
||||||
if (name) {
|
|
||||||
await removeSnapshot(name)
|
|
||||||
devicesInfo.snapshotInfo.list.splice(devicesInfo.snapshotInfo.list.indexOf(name), 1)
|
|
||||||
} else break
|
|
||||||
}
|
|
||||||
if (requiredSave) saveSnapshotInfo(devicesInfo.snapshotInfo)
|
|
||||||
}
|
|
||||||
export const updateDeviceSnapshotKey = (keyInfo: LX.Sync.ServerKeyInfo, key: string) => {
|
|
||||||
if (keyInfo.snapshotKey) deviceKeys.splice(deviceKeys.indexOf(keyInfo.snapshotKey), 1)
|
|
||||||
keyInfo.snapshotKey = key
|
|
||||||
keyInfo.lastSyncDate = Date.now()
|
|
||||||
saveClientKeyInfo(keyInfo)
|
|
||||||
deviceKeys.push(key)
|
|
||||||
saveDevicesInfoThrottle()
|
|
||||||
void clearOldSnapshot()
|
|
||||||
}
|
|
||||||
|
|
||||||
const saveSnapshotInfoThrottle = throttle(() => {
|
|
||||||
const store = getStore(STORE_NAMES.SYNC)
|
|
||||||
store.set('snapshotInfo', devicesInfo.snapshotInfo)
|
|
||||||
})
|
|
||||||
export const getSnapshotInfo = (): SnapshotInfo => {
|
|
||||||
return devicesInfo.snapshotInfo
|
|
||||||
}
|
|
||||||
export const saveSnapshotInfo = (info: SnapshotInfo) => {
|
|
||||||
devicesInfo.snapshotInfo = info
|
|
||||||
saveSnapshotInfoThrottle()
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getSnapshot = async(name: string) => {
|
|
||||||
console.log('getSnapshot', name)
|
|
||||||
const filePath = path.join(global.lxDataPath, `snapshot_${name}`)
|
|
||||||
let listData: LX.Sync.ListData
|
|
||||||
try {
|
|
||||||
listData = JSON.parse((await fs.promises.readFile(filePath)).toString('utf-8'))
|
|
||||||
} catch (err) {
|
|
||||||
log.warn(err)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return listData
|
|
||||||
}
|
|
||||||
export const saveSnapshot = async(name: string, data: string) => {
|
|
||||||
console.log('saveSnapshot', name)
|
|
||||||
const filePath = path.join(global.lxDataPath, `snapshot_${name}`)
|
|
||||||
return fs.promises.writeFile(filePath, data).catch((err) => {
|
|
||||||
log.error(err)
|
|
||||||
throw err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
export const removeSnapshot = async(name: string) => {
|
|
||||||
console.log('removeSnapshot', name)
|
|
||||||
const filePath = path.join(global.lxDataPath, `snapshot_${name}`)
|
|
||||||
return fs.promises.unlink(filePath).catch((err) => {
|
|
||||||
log.error(err)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { File } from './constants'
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
|
||||||
|
interface ServerKeyInfo {
|
||||||
|
clientId: string
|
||||||
|
key: string
|
||||||
|
deviceName: string
|
||||||
|
lastSyncDate?: number
|
||||||
|
snapshotKey?: string
|
||||||
|
lastConnectDate?: number
|
||||||
|
isMobile: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const exists = async(path: string) => fs.promises.stat(path).then(() => true).catch(() => false)
|
||||||
|
|
||||||
|
// 迁移 v2 sync 数据
|
||||||
|
export default async(dataPath: string) => {
|
||||||
|
const syncDataPath = path.join(dataPath, 'sync')
|
||||||
|
// console.log(syncDataPath)
|
||||||
|
if (await exists(syncDataPath)) return
|
||||||
|
const oldInfoPath = path.join(dataPath, 'sync.json')
|
||||||
|
// console.log(oldInfoPath)
|
||||||
|
if (!await exists(oldInfoPath)) return
|
||||||
|
const serverSyncDataPath = path.join(dataPath, File.serverDataPath)
|
||||||
|
const clientSyncDataPath = path.join(dataPath, File.clientDataPath)
|
||||||
|
|
||||||
|
await fs.promises.mkdir(serverSyncDataPath, { recursive: true })
|
||||||
|
await fs.promises.mkdir(clientSyncDataPath, { recursive: true })
|
||||||
|
const info = JSON.parse((await fs.promises.readFile(oldInfoPath)).toString())
|
||||||
|
|
||||||
|
|
||||||
|
const serverInfoPath = path.join(serverSyncDataPath, File.serverInfoJSON)
|
||||||
|
const devicesInfoPath = path.join(serverSyncDataPath, File.userDevicesJSON)
|
||||||
|
const listDir = path.join(serverSyncDataPath, File.listDir)
|
||||||
|
await fs.promises.mkdir(listDir)
|
||||||
|
|
||||||
|
|
||||||
|
const snapshotInfo = info.snapshotInfo
|
||||||
|
delete info.snapshotInfo
|
||||||
|
snapshotInfo.clients = {}
|
||||||
|
for (const device of Object.values<ServerKeyInfo>(info.clients)) {
|
||||||
|
snapshotInfo.clients[device.clientId] = {
|
||||||
|
snapshotKey: device.snapshotKey,
|
||||||
|
lastSyncDate: device.lastSyncDate,
|
||||||
|
}
|
||||||
|
device.lastConnectDate = device.lastSyncDate
|
||||||
|
delete device.lastSyncDate
|
||||||
|
delete device.snapshotKey
|
||||||
|
}
|
||||||
|
const devicesInfo = {
|
||||||
|
userName: 'default',
|
||||||
|
clients: info.clients,
|
||||||
|
}
|
||||||
|
await fs.promises.writeFile(serverInfoPath, JSON.stringify({ serverId: info.serverId, version: 2 }))
|
||||||
|
await fs.promises.writeFile(devicesInfoPath, JSON.stringify(devicesInfo))
|
||||||
|
await fs.promises.writeFile(path.join(listDir, File.listSnapshotInfoJSON), JSON.stringify(snapshotInfo))
|
||||||
|
|
||||||
|
const snapshotPath = path.join(listDir, File.listSnapshotDir)
|
||||||
|
await fs.promises.mkdir(snapshotPath)
|
||||||
|
const snapshots = (await fs.promises.readdir(dataPath)).filter(name => name.startsWith('snapshot_'))
|
||||||
|
if (snapshots.length) {
|
||||||
|
for (const file of snapshots) {
|
||||||
|
await fs.promises.copyFile(path.join(dataPath, file), path.join(snapshotPath, file))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await fs.promises.writeFile(path.join(clientSyncDataPath, File.syncAuthKeysJSON), JSON.stringify(info.syncAuthKey))
|
||||||
|
|
||||||
|
for (const file of snapshots) {
|
||||||
|
await fs.promises.unlink(path.join(dataPath, file))
|
||||||
|
}
|
||||||
|
await fs.promises.unlink(oldInfoPath)
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import * as modules from './modules'
|
|
||||||
import {
|
import {
|
||||||
startServer,
|
startServer,
|
||||||
stopServer,
|
stopServer,
|
||||||
|
@ -12,5 +11,4 @@ export {
|
||||||
stopServer,
|
stopServer,
|
||||||
getStatus,
|
getStatus,
|
||||||
generateCode,
|
generateCode,
|
||||||
modules,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import * as list from './list'
|
import { sync } from './list'
|
||||||
// export * as theme from './theme'
|
|
||||||
|
|
||||||
|
|
||||||
export const callObj = Object.assign({}, list.handler)
|
|
||||||
|
|
||||||
|
export const callObj = Object.assign({},
|
||||||
|
sync.handler,
|
||||||
|
)
|
||||||
|
|
||||||
export const modules = {
|
export const modules = {
|
||||||
list,
|
list: sync,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export { ListManage } from './list'
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
// 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)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,4 +1,3 @@
|
||||||
export * as handler from './handler'
|
export * as sync from './sync'
|
||||||
export { default as sync } from './sync'
|
export { ListManage } from './manage'
|
||||||
|
|
||||||
export * from './localEvent'
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { type UserDataManage } from '../../user'
|
||||||
|
import { SnapshotDataManage } from './snapshotDataManage'
|
||||||
|
import { toMD5 } from '../../utils'
|
||||||
|
import { getLocalListData } from '@main/modules/sync/utils'
|
||||||
|
|
||||||
|
export class ListManage {
|
||||||
|
snapshotDataManage: SnapshotDataManage
|
||||||
|
|
||||||
|
constructor(userDataManage: UserDataManage) {
|
||||||
|
this.snapshotDataManage = new SnapshotDataManage(userDataManage)
|
||||||
|
}
|
||||||
|
|
||||||
|
createSnapshot = async() => {
|
||||||
|
const listData = JSON.stringify(await this.getListData())
|
||||||
|
const md5 = toMD5(listData)
|
||||||
|
const snapshotInfo = await this.snapshotDataManage.getSnapshotInfo()
|
||||||
|
console.log(md5, snapshotInfo.latest)
|
||||||
|
if (snapshotInfo.latest == md5) return md5
|
||||||
|
if (snapshotInfo.list.includes(md5)) {
|
||||||
|
snapshotInfo.list.splice(snapshotInfo.list.indexOf(md5), 1)
|
||||||
|
} else await this.snapshotDataManage.saveSnapshot(md5, listData)
|
||||||
|
if (snapshotInfo.latest) snapshotInfo.list.unshift(snapshotInfo.latest)
|
||||||
|
snapshotInfo.latest = md5
|
||||||
|
snapshotInfo.time = Date.now()
|
||||||
|
this.snapshotDataManage.saveSnapshotInfo(snapshotInfo)
|
||||||
|
return md5
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentListInfoKey = async() => {
|
||||||
|
const snapshotInfo = await this.snapshotDataManage.getSnapshotInfo()
|
||||||
|
if (snapshotInfo.latest) {
|
||||||
|
return snapshotInfo.latest
|
||||||
|
}
|
||||||
|
snapshotInfo.latest = toMD5(JSON.stringify(await this.getListData()))
|
||||||
|
this.snapshotDataManage.saveSnapshotInfo(snapshotInfo)
|
||||||
|
return snapshotInfo.latest
|
||||||
|
}
|
||||||
|
|
||||||
|
getDeviceCurrentSnapshotKey = async(clientId: string) => {
|
||||||
|
return this.snapshotDataManage.getDeviceCurrentSnapshotKey(clientId)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDeviceSnapshotKey = async(clientId: string, key: string) => {
|
||||||
|
await this.snapshotDataManage.updateDeviceSnapshotKey(clientId, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
getListData = async(): Promise<LX.Sync.ListData> => {
|
||||||
|
return getLocalListData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
import { throttle } from '@common/utils/common'
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
import syncLog from '../../../log'
|
||||||
|
import { getUserConfig, type UserDataManage } from '../../user/data'
|
||||||
|
import { File } from '../../../constants'
|
||||||
|
import { checkAndCreateDirSync } from '../../utils'
|
||||||
|
|
||||||
|
|
||||||
|
interface SnapshotInfo {
|
||||||
|
latest: string | null
|
||||||
|
time: number
|
||||||
|
list: string[]
|
||||||
|
clients: Record<string, LX.Sync.ListInfo>
|
||||||
|
}
|
||||||
|
export class SnapshotDataManage {
|
||||||
|
userDataManage: UserDataManage
|
||||||
|
listDir: string
|
||||||
|
snapshotDir: string
|
||||||
|
snapshotInfoFilePath: string
|
||||||
|
snapshotInfo: SnapshotInfo
|
||||||
|
clientSnapshotKeys: string[]
|
||||||
|
private readonly saveSnapshotInfoThrottle: () => void
|
||||||
|
|
||||||
|
isIncluedsDevice = (key: string) => {
|
||||||
|
return this.clientSnapshotKeys.includes(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
clearOldSnapshot = async() => {
|
||||||
|
if (!this.snapshotInfo) return
|
||||||
|
const snapshotList = this.snapshotInfo.list.filter(key => !this.isIncluedsDevice(key))
|
||||||
|
// console.log(snapshotList.length, lx.config.maxSnapshotNum)
|
||||||
|
const userMaxSnapshotNum = getUserConfig(this.userDataManage.userName).maxSnapshotNum
|
||||||
|
let requiredSave = snapshotList.length > userMaxSnapshotNum
|
||||||
|
while (snapshotList.length > userMaxSnapshotNum) {
|
||||||
|
const name = snapshotList.pop()
|
||||||
|
if (name) {
|
||||||
|
await this.removeSnapshot(name)
|
||||||
|
this.snapshotInfo.list.splice(this.snapshotInfo.list.indexOf(name), 1)
|
||||||
|
} else break
|
||||||
|
}
|
||||||
|
if (requiredSave) this.saveSnapshotInfo(this.snapshotInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDeviceSnapshotKey = async(clientId: string, key: string) => {
|
||||||
|
// console.log('updateDeviceSnapshotKey', key)
|
||||||
|
let client = this.snapshotInfo.clients[clientId]
|
||||||
|
if (!client) client = this.snapshotInfo.clients[clientId] = { snapshotKey: '', lastSyncDate: 0 }
|
||||||
|
if (client.snapshotKey) this.clientSnapshotKeys.splice(this.clientSnapshotKeys.indexOf(client.snapshotKey), 1)
|
||||||
|
client.snapshotKey = key
|
||||||
|
client.lastSyncDate = Date.now()
|
||||||
|
this.clientSnapshotKeys.push(key)
|
||||||
|
this.saveSnapshotInfoThrottle()
|
||||||
|
}
|
||||||
|
|
||||||
|
getDeviceCurrentSnapshotKey = async(clientId: string) => {
|
||||||
|
// console.log('updateDeviceSnapshotKey', key)
|
||||||
|
const client = this.snapshotInfo.clients[clientId]
|
||||||
|
return client?.snapshotKey
|
||||||
|
}
|
||||||
|
|
||||||
|
getSnapshotInfo = async(): Promise<SnapshotInfo> => {
|
||||||
|
return this.snapshotInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
saveSnapshotInfo = (info: SnapshotInfo) => {
|
||||||
|
this.snapshotInfo = info
|
||||||
|
this.saveSnapshotInfoThrottle()
|
||||||
|
}
|
||||||
|
|
||||||
|
getSnapshot = async(name: string) => {
|
||||||
|
const filePath = path.join(this.snapshotDir, `snapshot_${name}`)
|
||||||
|
let listData: LX.Sync.ListData
|
||||||
|
try {
|
||||||
|
listData = JSON.parse((await fs.promises.readFile(filePath)).toString('utf-8'))
|
||||||
|
} catch (err) {
|
||||||
|
syncLog.warn(err)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return listData
|
||||||
|
}
|
||||||
|
|
||||||
|
saveSnapshot = async(name: string, data: string) => {
|
||||||
|
syncLog.info('saveSnapshot', this.userDataManage.userName, name)
|
||||||
|
const filePath = path.join(this.snapshotDir, `snapshot_${name}`)
|
||||||
|
try {
|
||||||
|
await fs.promises.writeFile(filePath, data)
|
||||||
|
} catch (err) {
|
||||||
|
syncLog.error(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSnapshot = async(name: string) => {
|
||||||
|
syncLog.info('removeSnapshot', this.userDataManage.userName, name)
|
||||||
|
const filePath = path.join(this.snapshotDir, `snapshot_${name}`)
|
||||||
|
try {
|
||||||
|
await fs.promises.unlink(filePath)
|
||||||
|
} catch (err) {
|
||||||
|
syncLog.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor(userDataManage: UserDataManage) {
|
||||||
|
this.userDataManage = userDataManage
|
||||||
|
|
||||||
|
this.listDir = path.join(userDataManage.userDir, File.listDir)
|
||||||
|
checkAndCreateDirSync(this.listDir)
|
||||||
|
|
||||||
|
this.snapshotDir = path.join(this.listDir, File.listSnapshotDir)
|
||||||
|
checkAndCreateDirSync(this.snapshotDir)
|
||||||
|
|
||||||
|
this.snapshotInfoFilePath = path.join(this.listDir, File.listSnapshotInfoJSON)
|
||||||
|
this.snapshotInfo = fs.existsSync(this.snapshotInfoFilePath)
|
||||||
|
? JSON.parse(fs.readFileSync(this.snapshotInfoFilePath).toString())
|
||||||
|
: { latest: null, time: 0, list: [], clients: {} }
|
||||||
|
|
||||||
|
this.saveSnapshotInfoThrottle = throttle(() => {
|
||||||
|
fs.writeFile(this.snapshotInfoFilePath, JSON.stringify(this.snapshotInfo), 'utf8', (err) => {
|
||||||
|
if (err) console.error(err)
|
||||||
|
void this.clearOldSnapshot()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.clientSnapshotKeys = Object.values(this.snapshotInfo.clients).map(device => device.snapshotKey).filter(k => k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// type UserDataManages = Map<string, UserDataManage>
|
||||||
|
|
||||||
|
// export const createUserDataManage = (user: LX.UserConfig) => {
|
||||||
|
// const manage = Object.create(userDataManage) as typeof userDataManage
|
||||||
|
// manage.userDir = user.dataPath
|
||||||
|
// }
|
|
@ -0,0 +1,163 @@
|
||||||
|
// import { throttle } from '@common/utils/common'
|
||||||
|
// import { sendSyncActionList } from '@main/modules/winMain'
|
||||||
|
// import { SYNC_CLOSE_CODE } from '@/constants'
|
||||||
|
import { SYNC_CLOSE_CODE } from '@main/modules/sync/constants'
|
||||||
|
import { getUserSpace } from '@main/modules/sync/server/user'
|
||||||
|
import { handleRemoteListAction } from '@main/modules/sync/utils'
|
||||||
|
// import { encryptMsg } from '@/utils/tools'
|
||||||
|
|
||||||
|
// let wss: LX.SocketServer | null
|
||||||
|
// let removeListener: (() => void) | null
|
||||||
|
|
||||||
|
// type listAction = 'list:action'
|
||||||
|
|
||||||
|
// const registerListActionEvent = () => {
|
||||||
|
// const list_data_overwrite = async(listData: MakeOptional<LX.List.ListDataFull, 'tempList'>, isRemote: boolean = false) => {
|
||||||
|
// if (isRemote) return
|
||||||
|
// await sendListAction({ action: 'list_data_overwrite', data: listData })
|
||||||
|
// }
|
||||||
|
// const list_create = async(position: number, listInfos: LX.List.UserListInfo[], isRemote: boolean = false) => {
|
||||||
|
// if (isRemote) return
|
||||||
|
// await sendListAction({ action: 'list_create', data: { position, listInfos } })
|
||||||
|
// }
|
||||||
|
// const list_remove = async(ids: string[], isRemote: boolean = false) => {
|
||||||
|
// if (isRemote) return
|
||||||
|
// await sendListAction({ action: 'list_remove', data: ids })
|
||||||
|
// }
|
||||||
|
// const list_update = async(lists: LX.List.UserListInfo[], isRemote: boolean = false) => {
|
||||||
|
// if (isRemote) return
|
||||||
|
// await sendListAction({ action: 'list_update', data: lists })
|
||||||
|
// }
|
||||||
|
// const list_update_position = async(position: number, ids: string[], isRemote: boolean = false) => {
|
||||||
|
// if (isRemote) return
|
||||||
|
// await sendListAction({ action: 'list_update_position', data: { position, ids } })
|
||||||
|
// }
|
||||||
|
// const list_music_overwrite = async(listId: string, musicInfos: LX.Music.MusicInfo[], isRemote: boolean = false) => {
|
||||||
|
// if (isRemote) return
|
||||||
|
// await sendListAction({ action: 'list_music_overwrite', data: { listId, musicInfos } })
|
||||||
|
// }
|
||||||
|
// const list_music_add = async(id: string, musicInfos: LX.Music.MusicInfo[], addMusicLocationType: LX.AddMusicLocationType, isRemote: boolean = false) => {
|
||||||
|
// if (isRemote) return
|
||||||
|
// await sendListAction({ action: 'list_music_add', data: { id, musicInfos, addMusicLocationType } })
|
||||||
|
// }
|
||||||
|
// const list_music_move = async(fromId: string, toId: string, musicInfos: LX.Music.MusicInfo[], addMusicLocationType: LX.AddMusicLocationType, isRemote: boolean = false) => {
|
||||||
|
// if (isRemote) return
|
||||||
|
// await sendListAction({ action: 'list_music_move', data: { fromId, toId, musicInfos, addMusicLocationType } })
|
||||||
|
// }
|
||||||
|
// const list_music_remove = async(listId: string, ids: string[], isRemote: boolean = false) => {
|
||||||
|
// if (isRemote) return
|
||||||
|
// await sendListAction({ action: 'list_music_remove', data: { listId, ids } })
|
||||||
|
// }
|
||||||
|
// const list_music_update = async(musicInfos: LX.List.ListActionMusicUpdate, isRemote: boolean = false) => {
|
||||||
|
// if (isRemote) return
|
||||||
|
// await sendListAction({ action: 'list_music_update', data: musicInfos })
|
||||||
|
// }
|
||||||
|
// const list_music_clear = async(ids: string[], isRemote: boolean = false) => {
|
||||||
|
// if (isRemote) return
|
||||||
|
// await sendListAction({ action: 'list_music_clear', data: ids })
|
||||||
|
// }
|
||||||
|
// const list_music_update_position = async(listId: string, position: number, ids: string[], isRemote: boolean = false) => {
|
||||||
|
// if (isRemote) return
|
||||||
|
// await sendListAction({ action: 'list_music_update_position', data: { listId, position, ids } })
|
||||||
|
// }
|
||||||
|
// global.event_list.on('list_data_overwrite', list_data_overwrite)
|
||||||
|
// global.event_list.on('list_create', list_create)
|
||||||
|
// global.event_list.on('list_remove', list_remove)
|
||||||
|
// global.event_list.on('list_update', list_update)
|
||||||
|
// global.event_list.on('list_update_position', list_update_position)
|
||||||
|
// global.event_list.on('list_music_overwrite', list_music_overwrite)
|
||||||
|
// global.event_list.on('list_music_add', list_music_add)
|
||||||
|
// global.event_list.on('list_music_move', list_music_move)
|
||||||
|
// global.event_list.on('list_music_remove', list_music_remove)
|
||||||
|
// global.event_list.on('list_music_update', list_music_update)
|
||||||
|
// global.event_list.on('list_music_clear', list_music_clear)
|
||||||
|
// global.event_list.on('list_music_update_position', list_music_update_position)
|
||||||
|
// return () => {
|
||||||
|
// global.event_list.off('list_data_overwrite', list_data_overwrite)
|
||||||
|
// global.event_list.off('list_create', list_create)
|
||||||
|
// global.event_list.off('list_remove', list_remove)
|
||||||
|
// global.event_list.off('list_update', list_update)
|
||||||
|
// global.event_list.off('list_update_position', list_update_position)
|
||||||
|
// global.event_list.off('list_music_overwrite', list_music_overwrite)
|
||||||
|
// global.event_list.off('list_music_add', list_music_add)
|
||||||
|
// global.event_list.off('list_music_move', list_music_move)
|
||||||
|
// global.event_list.off('list_music_remove', list_music_remove)
|
||||||
|
// global.event_list.off('list_music_update', list_music_update)
|
||||||
|
// global.event_list.off('list_music_clear', list_music_clear)
|
||||||
|
// global.event_list.off('list_music_update_position', list_music_update_position)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const addMusic = (orderId, callback) => {
|
||||||
|
// // ...
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const broadcast = async(socket: LX.Socket, key: string, data: any, excludeIds: string[] = []) => {
|
||||||
|
// if (!wss) return
|
||||||
|
// const dataStr = JSON.stringify({ action: 'list:sync:action', data })
|
||||||
|
// const userSpace = getUserSpace(socket.userInfo.name)
|
||||||
|
// for (const client of wss.clients) {
|
||||||
|
// if (excludeIds.includes(client.keyInfo.clientId) || !client.isReady || client.userInfo.name != socket.userInfo.name) continue
|
||||||
|
// client.send(encryptMsg(client.keyInfo, dataStr), (err) => {
|
||||||
|
// if (err) {
|
||||||
|
// client.close(SYNC_CLOSE_CODE.failed)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// userSpace.dataManage.updateDeviceSnapshotKey(client.keyInfo, key)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export const sendListAction = async(action: LX.Sync.ActionList) => {
|
||||||
|
// console.log('sendListAction', action.action)
|
||||||
|
// // io.sockets
|
||||||
|
// await broadcast('list:sync:action', action)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export const registerListHandler = (_wss: LX.SocketServer, socket: LX.Socket) => {
|
||||||
|
// if (!wss) {
|
||||||
|
// wss = _wss
|
||||||
|
// // removeListener = registerListActionEvent()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const userSpace = getUserSpace(socket.userInfo.name)
|
||||||
|
// socket.onRemoteEvent('list:sync:action', (action) => {
|
||||||
|
// if (!socket.isReady) return
|
||||||
|
// // console.log(msg)
|
||||||
|
// void handleListAction(socket.userInfo.name, action).then(key => {
|
||||||
|
// if (!key) return
|
||||||
|
// console.log(key)
|
||||||
|
// userSpace.dataManage.updateDeviceSnapshotKey(socket.keyInfo, key)
|
||||||
|
// void broadcast(socket, 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) => {
|
||||||
|
const userSpace = getUserSpace(socket.userInfo.name)
|
||||||
|
await handleRemoteListAction(action).then(async updated => {
|
||||||
|
if (!updated) {
|
||||||
|
socket.close(SYNC_CLOSE_CODE.failed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const key = await userSpace.listManage.createSnapshot()
|
||||||
|
userSpace.listManage.updateDeviceSnapshotKey(socket.keyInfo.clientId, key)
|
||||||
|
const currentUserName = socket.userInfo.name
|
||||||
|
const currentId = socket.keyInfo.clientId
|
||||||
|
socket.broadcast((client) => {
|
||||||
|
if (client.keyInfo.clientId == currentId || !client.isReady || client.userInfo.name != currentUserName) return
|
||||||
|
void client.remoteSyncList.onListSyncAction(action)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * as handler from './handler'
|
||||||
|
export { default as sync } from './sync'
|
||||||
|
export * from './localEvent'
|
|
@ -1,6 +1,5 @@
|
||||||
import { updateDeviceSnapshotKey } from '@main/modules/sync/data'
|
import { registerListActionEvent } from '../../../../utils'
|
||||||
import { registerListActionEvent } from '../../../utils'
|
import { getUserSpace } from '../../../user'
|
||||||
import { getCurrentListInfoKey } from '../../utils'
|
|
||||||
|
|
||||||
// let socket: LX.Sync.Server.Socket | null
|
// let socket: LX.Sync.Server.Socket | null
|
||||||
let unregisterLocalListAction: (() => void) | null
|
let unregisterLocalListAction: (() => void) | null
|
||||||
|
@ -8,11 +7,12 @@ let unregisterLocalListAction: (() => void) | null
|
||||||
|
|
||||||
const sendListAction = async(wss: LX.Sync.Server.SocketServer, action: LX.Sync.ActionList) => {
|
const sendListAction = async(wss: LX.Sync.Server.SocketServer, action: LX.Sync.ActionList) => {
|
||||||
// console.log('sendListAction', action.action)
|
// console.log('sendListAction', action.action)
|
||||||
const key = await getCurrentListInfoKey()
|
const userSpace = getUserSpace()
|
||||||
|
const key = await userSpace.listManage.createSnapshot()
|
||||||
for (const client of wss.clients) {
|
for (const client of wss.clients) {
|
||||||
if (!client.isReady) return
|
if (!client.isReady) return
|
||||||
void client.remoteSyncList.onListSyncAction(action).then(() => {
|
void client.remoteSyncList.onListSyncAction(action).then(() => {
|
||||||
updateDeviceSnapshotKey(client.keyInfo, key)
|
void userSpace.listManage.updateDeviceSnapshotKey(client.keyInfo.clientId, key)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,7 @@
|
||||||
import { SYNC_CLOSE_CODE } from '@common/constants'
|
import { SYNC_CLOSE_CODE } from '../../../../constants'
|
||||||
|
import { getUserSpace, getUserConfig } from '../../../user'
|
||||||
import { getLocalListData, setLocalListData } from '@main/modules/sync/utils'
|
import { getLocalListData, setLocalListData } from '@main/modules/sync/utils'
|
||||||
import { removeSelectModeListener, sendCloseSelectMode, sendSelectMode } from '@main/modules/winMain'
|
// import { LIST_IDS } from '@common/constants'
|
||||||
import { createSnapshot, getCurrentListInfoKey } from '../../utils'
|
|
||||||
import { getSnapshot, updateDeviceSnapshotKey } from '@main/modules/sync/data'
|
|
||||||
|
|
||||||
|
|
||||||
const handleSetLocalListData = async(listData: LX.Sync.ListData) => {
|
|
||||||
await setLocalListData(listData)
|
|
||||||
return createSnapshot()
|
|
||||||
}
|
|
||||||
|
|
||||||
// type ListInfoType = LX.List.UserListInfoFull | LX.List.MyDefaultListInfoFull | LX.List.MyLoveListInfoFull
|
// type ListInfoType = LX.List.UserListInfoFull | LX.List.MyDefaultListInfoFull | LX.List.MyLoveListInfoFull
|
||||||
|
|
||||||
|
@ -33,35 +26,32 @@ const getRemoteListMD5 = async(socket: LX.Sync.Server.Socket): Promise<string> =
|
||||||
return socket.remoteSyncList.list_sync_get_md5()
|
return socket.remoteSyncList.list_sync_get_md5()
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSyncMode = async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.Mode> => new Promise((resolve, reject) => {
|
// const getLocalListData = async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.ListData> => {
|
||||||
const handleDisconnect = (err: Error) => {
|
// return getUserSpace(socket.userInfo.name).listManage.getListData()
|
||||||
sendCloseSelectMode()
|
// }
|
||||||
removeSelectModeListener()
|
const getSyncMode = async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.ListSyncMode> => {
|
||||||
reject(err)
|
return socket.remoteSyncList.list_sync_get_sync_mode()
|
||||||
}
|
}
|
||||||
let removeEventClose = socket.onClose(handleDisconnect)
|
|
||||||
sendSelectMode(socket.keyInfo.deviceName, (mode) => {
|
|
||||||
if (mode == null) {
|
|
||||||
reject(new Error('cancel'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resolve(mode)
|
|
||||||
removeSelectModeListener()
|
|
||||||
removeEventClose()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const finishedSync = async(socket: LX.Sync.Server.Socket) => {
|
const finishedSync = async(socket: LX.Sync.Server.Socket) => {
|
||||||
await socket.remoteSyncList.list_sync_finished()
|
await socket.remoteSyncList.list_sync_finished()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const setLocalList = async(socket: LX.Sync.Server.Socket, listData: LX.Sync.ListData) => {
|
||||||
|
await setLocalListData(listData)
|
||||||
|
const userSpace = getUserSpace(socket.userInfo.name)
|
||||||
|
return userSpace.listManage.createSnapshot()
|
||||||
|
}
|
||||||
|
|
||||||
const overwriteRemoteListData = async(socket: LX.Sync.Server.Socket, listData: LX.Sync.ListData, key: string, excludeIds: string[] = []) => {
|
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 action = { action: 'list_data_overwrite', data: listData } as const
|
||||||
const tasks: Array<Promise<void>> = []
|
const tasks: Array<Promise<void>> = []
|
||||||
socket.broadcast((client) => {
|
socket.broadcast((client) => {
|
||||||
if (excludeIds.includes(client.keyInfo.clientId) || !client.isReady) return
|
if (excludeIds.includes(client.keyInfo.clientId) || client.userInfo.name != socket.userInfo.name || !client.isReady) return
|
||||||
tasks.push(client.remoteSyncList.onListSyncAction(action).then(() => {
|
tasks.push(client.remoteSyncList.onListSyncAction(action).then(async() => {
|
||||||
updateDeviceSnapshotKey(socket.keyInfo, key)
|
const userSpace = getUserSpace(socket.userInfo.name)
|
||||||
|
return userSpace.listManage.updateDeviceSnapshotKey(socket.keyInfo.clientId, key)
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.log(err.message)
|
console.log(err.message)
|
||||||
}))
|
}))
|
||||||
|
@ -71,7 +61,8 @@ const overwriteRemoteListData = async(socket: LX.Sync.Server.Socket, listData: L
|
||||||
}
|
}
|
||||||
const setRemotelList = async(socket: LX.Sync.Server.Socket, listData: LX.Sync.ListData, key: string): Promise<void> => {
|
const setRemotelList = async(socket: LX.Sync.Server.Socket, listData: LX.Sync.ListData, key: string): Promise<void> => {
|
||||||
await socket.remoteSyncList.list_sync_set_list_data(listData)
|
await socket.remoteSyncList.list_sync_set_list_data(listData)
|
||||||
updateDeviceSnapshotKey(socket.keyInfo, key)
|
const userSpace = getUserSpace(socket.userInfo.name)
|
||||||
|
await userSpace.listManage.updateDeviceSnapshotKey(socket.keyInfo.clientId, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserDataObj = Map<string, LX.List.UserListInfoFull>
|
type UserDataObj = Map<string, LX.List.UserListInfoFull>
|
||||||
|
@ -120,8 +111,8 @@ const handleMergeList = (
|
||||||
}
|
}
|
||||||
return ids.map(id => map.get(id)) as LX.Music.MusicInfo[]
|
return ids.map(id => map.get(id)) as LX.Music.MusicInfo[]
|
||||||
}
|
}
|
||||||
const mergeList = (sourceListData: LX.Sync.ListData, targetListData: LX.Sync.ListData): LX.Sync.ListData => {
|
const mergeList = (socket: LX.Sync.Server.Socket, sourceListData: LX.Sync.ListData, targetListData: LX.Sync.ListData): LX.Sync.ListData => {
|
||||||
const addMusicLocationType = global.lx.appSetting['list.addMusicLocationType']
|
const addMusicLocationType = getUserConfig(socket.userInfo.name)['list.addMusicLocationType']
|
||||||
const newListData: LX.Sync.ListData = {
|
const newListData: LX.Sync.ListData = {
|
||||||
defaultList: [],
|
defaultList: [],
|
||||||
loveList: [],
|
loveList: [],
|
||||||
|
@ -181,7 +172,7 @@ const overwriteList = (sourceListData: LX.Sync.ListData, targetListData: LX.Sync
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMergeListData = async(socket: LX.Sync.Server.Socket): Promise<[LX.Sync.ListData, boolean, boolean]> => {
|
const handleMergeListData = async(socket: LX.Sync.Server.Socket): Promise<[LX.Sync.ListData, boolean, boolean]> => {
|
||||||
const mode: LX.Sync.Mode = await getSyncMode(socket)
|
const mode: LX.Sync.ListSyncMode = await getSyncMode(socket)
|
||||||
|
|
||||||
if (mode == 'cancel') {
|
if (mode == 'cancel') {
|
||||||
socket.close(SYNC_CLOSE_CODE.normal)
|
socket.close(SYNC_CLOSE_CODE.normal)
|
||||||
|
@ -194,10 +185,10 @@ const handleMergeListData = async(socket: LX.Sync.Server.Socket): Promise<[LX.Sy
|
||||||
let requiredUpdateRemoteListData = true
|
let requiredUpdateRemoteListData = true
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'merge_local_remote':
|
case 'merge_local_remote':
|
||||||
listData = mergeList(localListData, remoteListData)
|
listData = mergeList(socket, localListData, remoteListData)
|
||||||
break
|
break
|
||||||
case 'merge_remote_local':
|
case 'merge_remote_local':
|
||||||
listData = mergeList(remoteListData, localListData)
|
listData = mergeList(socket, remoteListData, localListData)
|
||||||
break
|
break
|
||||||
case 'overwrite_local_remote':
|
case 'overwrite_local_remote':
|
||||||
listData = overwriteList(localListData, remoteListData)
|
listData = overwriteList(localListData, remoteListData)
|
||||||
|
@ -225,31 +216,35 @@ const handleMergeListData = async(socket: LX.Sync.Server.Socket): Promise<[LX.Sy
|
||||||
const handleSyncList = async(socket: LX.Sync.Server.Socket) => {
|
const handleSyncList = async(socket: LX.Sync.Server.Socket) => {
|
||||||
const [remoteListData, localListData] = await Promise.all([getRemoteListData(socket), getLocalListData()])
|
const [remoteListData, localListData] = await Promise.all([getRemoteListData(socket), getLocalListData()])
|
||||||
console.log('handleSyncList', 'remoteListData, localListData')
|
console.log('handleSyncList', 'remoteListData, localListData')
|
||||||
|
console.log('localListData', localListData.defaultList.length || localListData.loveList.length || localListData.userList.length)
|
||||||
|
console.log('remoteListData', remoteListData.defaultList.length || remoteListData.loveList.length || remoteListData.userList.length)
|
||||||
|
const userSpace = getUserSpace(socket.userInfo.name)
|
||||||
|
const clientId = socket.keyInfo.clientId
|
||||||
if (localListData.defaultList.length || localListData.loveList.length || localListData.userList.length) {
|
if (localListData.defaultList.length || localListData.loveList.length || localListData.userList.length) {
|
||||||
if (remoteListData.defaultList.length || remoteListData.loveList.length || remoteListData.userList.length) {
|
if (remoteListData.defaultList.length || remoteListData.loveList.length || remoteListData.userList.length) {
|
||||||
const [mergedList, requiredUpdateLocalListData, requiredUpdateRemoteListData] = await handleMergeListData(socket)
|
const [mergedList, requiredUpdateLocalListData, requiredUpdateRemoteListData] = await handleMergeListData(socket)
|
||||||
console.log('handleMergeListData', 'mergedList')
|
console.log('handleMergeListData', 'mergedList', requiredUpdateLocalListData, requiredUpdateRemoteListData)
|
||||||
let key
|
let key
|
||||||
if (requiredUpdateLocalListData) {
|
if (requiredUpdateLocalListData) {
|
||||||
key = await handleSetLocalListData(mergedList)
|
key = await setLocalList(socket, mergedList)
|
||||||
await overwriteRemoteListData(socket, mergedList, key, [socket.keyInfo.clientId])
|
await overwriteRemoteListData(socket, mergedList, key, [clientId])
|
||||||
if (!requiredUpdateRemoteListData) updateDeviceSnapshotKey(socket.keyInfo, key)
|
if (!requiredUpdateRemoteListData) await userSpace.listManage.updateDeviceSnapshotKey(clientId, key)
|
||||||
}
|
}
|
||||||
if (requiredUpdateRemoteListData) {
|
if (requiredUpdateRemoteListData) {
|
||||||
if (!key) key = await getCurrentListInfoKey()
|
if (!key) key = await userSpace.listManage.getCurrentListInfoKey()
|
||||||
await setRemotelList(socket, mergedList, key)
|
await setRemotelList(socket, mergedList, key)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await setRemotelList(socket, localListData, await getCurrentListInfoKey())
|
await setRemotelList(socket, localListData, await userSpace.listManage.getCurrentListInfoKey())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
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 setLocalList(socket, remoteListData)
|
||||||
await overwriteRemoteListData(socket, remoteListData, key, [socket.keyInfo.clientId])
|
await overwriteRemoteListData(socket, remoteListData, key, [clientId])
|
||||||
}
|
}
|
||||||
key ??= await getCurrentListInfoKey()
|
key ??= await userSpace.listManage.getCurrentListInfoKey()
|
||||||
updateDeviceSnapshotKey(socket.keyInfo, key)
|
await userSpace.listManage.updateDeviceSnapshotKey(clientId, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,15 +292,17 @@ const mergeListDataFromSnapshot = (
|
||||||
}
|
}
|
||||||
const checkListLatest = async(socket: LX.Sync.Server.Socket) => {
|
const checkListLatest = async(socket: LX.Sync.Server.Socket) => {
|
||||||
const remoteListMD5 = await getRemoteListMD5(socket)
|
const remoteListMD5 = await getRemoteListMD5(socket)
|
||||||
const currentListInfoKey = await getCurrentListInfoKey()
|
const userSpace = getUserSpace(socket.userInfo.name)
|
||||||
|
const userCurrentListInfoKey = await userSpace.listManage.getDeviceCurrentSnapshotKey(socket.keyInfo.clientId)
|
||||||
|
const currentListInfoKey = await userSpace.listManage.getCurrentListInfoKey()
|
||||||
const latest = remoteListMD5 == currentListInfoKey
|
const latest = remoteListMD5 == currentListInfoKey
|
||||||
if (latest && socket.keyInfo.snapshotKey != currentListInfoKey) updateDeviceSnapshotKey(socket.keyInfo, currentListInfoKey)
|
if (latest && userCurrentListInfoKey != currentListInfoKey) await userSpace.listManage.updateDeviceSnapshotKey(socket.keyInfo.clientId, currentListInfoKey)
|
||||||
return latest
|
return latest
|
||||||
}
|
}
|
||||||
const handleMergeListDataFromSnapshot = async(socket: LX.Sync.Server.Socket, snapshot: LX.Sync.ListData) => {
|
const handleMergeListDataFromSnapshot = async(socket: LX.Sync.Server.Socket, snapshot: LX.Sync.ListData) => {
|
||||||
if (await checkListLatest(socket)) return
|
if (await checkListLatest(socket)) return
|
||||||
|
|
||||||
const addMusicLocationType = global.lx.appSetting['list.addMusicLocationType']
|
const addMusicLocationType = getUserConfig(socket.userInfo.name)['list.addMusicLocationType']
|
||||||
const [remoteListData, localListData] = await Promise.all([getRemoteListData(socket), getLocalListData()])
|
const [remoteListData, localListData] = await Promise.all([getRemoteListData(socket), getLocalListData()])
|
||||||
const newListData: LX.Sync.ListData = {
|
const newListData: LX.Sync.ListData = {
|
||||||
defaultList: [],
|
defaultList: [],
|
||||||
|
@ -361,17 +358,21 @@ const handleMergeListDataFromSnapshot = async(socket: LX.Sync.Server.Socket, sna
|
||||||
})
|
})
|
||||||
|
|
||||||
newListData.userList = newUserList
|
newListData.userList = newUserList
|
||||||
const key = await handleSetLocalListData(newListData)
|
const key = await setLocalList(socket, newListData)
|
||||||
await setRemotelList(socket, newListData, key)
|
const err = await setRemotelList(socket, newListData, key).catch(err => err)
|
||||||
await overwriteRemoteListData(socket, newListData, key, [socket.keyInfo.clientId])
|
await overwriteRemoteListData(socket, newListData, key, [socket.keyInfo.clientId])
|
||||||
|
if (err) throw err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const syncList = async(socket: LX.Sync.Server.Socket) => {
|
const syncList = async(socket: LX.Sync.Server.Socket) => {
|
||||||
// socket.data.snapshotFilePath = getSnapshotFilePath(socket.keyInfo)
|
// socket.data.snapshotFilePath = getSnapshotFilePath(socket.keyInfo)
|
||||||
if (socket.keyInfo.snapshotKey) {
|
// console.log(socket.keyInfo)
|
||||||
const listData = await getSnapshot(socket.keyInfo.snapshotKey)
|
const user = getUserSpace(socket.userInfo.name)
|
||||||
|
const userCurrentListInfoKey = await user.listManage.getDeviceCurrentSnapshotKey(socket.keyInfo.clientId)
|
||||||
|
if (userCurrentListInfoKey) {
|
||||||
|
const listData = await user.listManage.snapshotDataManage.getSnapshot(userCurrentListInfoKey)
|
||||||
if (listData) {
|
if (listData) {
|
||||||
|
console.log('handleMergeListDataFromSnapshot')
|
||||||
await handleMergeListDataFromSnapshot(socket, listData)
|
await handleMergeListDataFromSnapshot(socket, listData)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -379,7 +380,6 @@ const syncList = async(socket: LX.Sync.Server.Socket) => {
|
||||||
await handleSyncList(socket)
|
await handleSyncList(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
|
||||||
|
@ -402,7 +402,6 @@ const syncList = async(socket: LX.Sync.Server.Socket) => {
|
||||||
|
|
||||||
// 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
|
|
@ -1,21 +1,26 @@
|
||||||
import type http from 'http'
|
import type http from 'http'
|
||||||
import { SYNC_CODE } from '@common/constants'
|
import { SYNC_CODE } from '@common/constants'
|
||||||
|
import {
|
||||||
|
aesEncrypt,
|
||||||
|
aesDecrypt,
|
||||||
|
rsaEncrypt,
|
||||||
|
getIP,
|
||||||
|
} from '../utils/tools'
|
||||||
import querystring from 'node:querystring'
|
import querystring from 'node:querystring'
|
||||||
import { getIP } from './utils'
|
import { getUserSpace, createClientKeyInfo } from '../user'
|
||||||
import { createClientKeyInfo, getClientKeyInfo, saveClientKeyInfo } from '../data'
|
import { toMD5 } from '../utils'
|
||||||
import { aesDecrypt, aesEncrypt, getComputerName, rsaEncrypt } from '../utils'
|
import { getComputerName } from '../../utils'
|
||||||
import { toMD5 } from '@common/utils/nodejs'
|
|
||||||
|
|
||||||
const requestIps = new Map<string, number>()
|
const requestIps = new Map<string, number>()
|
||||||
|
|
||||||
|
|
||||||
const getAvailableIP = (req: http.IncomingMessage) => {
|
const getAvailableIP = (req: http.IncomingMessage) => {
|
||||||
let ip = getIP(req)
|
let ip = getIP(req)
|
||||||
return ip && (requestIps.get(ip) ?? 0) < 10 ? ip : null
|
return ip && (requestIps.get(ip) ?? 0) < 10 ? ip : null
|
||||||
}
|
}
|
||||||
|
|
||||||
const verifyByKey = (encryptMsg: string, userId: string) => {
|
const verifyByKey = (encryptMsg: string, userId: string) => {
|
||||||
const keyInfo = getClientKeyInfo(userId)
|
const userSpace = getUserSpace()
|
||||||
|
const keyInfo = userSpace.dataManage.getClientKeyInfo(userId)
|
||||||
if (!keyInfo) return null
|
if (!keyInfo) return null
|
||||||
let text
|
let text
|
||||||
try {
|
try {
|
||||||
|
@ -28,7 +33,7 @@ const verifyByKey = (encryptMsg: string, userId: string) => {
|
||||||
const deviceName = text.replace(SYNC_CODE.authMsg, '') || 'Unknown'
|
const deviceName = text.replace(SYNC_CODE.authMsg, '') || 'Unknown'
|
||||||
if (deviceName != keyInfo.deviceName) {
|
if (deviceName != keyInfo.deviceName) {
|
||||||
keyInfo.deviceName = deviceName
|
keyInfo.deviceName = deviceName
|
||||||
saveClientKeyInfo(keyInfo)
|
userSpace.dataManage.saveClientKeyInfo(keyInfo)
|
||||||
}
|
}
|
||||||
return aesEncrypt(SYNC_CODE.helloMsg, keyInfo.key)
|
return aesEncrypt(SYNC_CODE.helloMsg, keyInfo.key)
|
||||||
}
|
}
|
||||||
|
@ -43,7 +48,7 @@ const verifyByCode = (encryptMsg: string, password: string) => {
|
||||||
let text
|
let text
|
||||||
try {
|
try {
|
||||||
text = aesDecrypt(encryptMsg, key)
|
text = aesDecrypt(encryptMsg, key)
|
||||||
} catch (err) {
|
} catch {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
// console.log(text)
|
// console.log(text)
|
||||||
|
@ -53,6 +58,8 @@ const verifyByCode = (encryptMsg: string, password: string) => {
|
||||||
const deviceName = data[2] || 'Unknown'
|
const deviceName = data[2] || 'Unknown'
|
||||||
const isMobile = data[3] == 'lx_music_mobile'
|
const isMobile = data[3] == 'lx_music_mobile'
|
||||||
const keyInfo = createClientKeyInfo(deviceName, isMobile)
|
const keyInfo = createClientKeyInfo(deviceName, isMobile)
|
||||||
|
const userSpace = getUserSpace()
|
||||||
|
userSpace.dataManage.saveClientKeyInfo(keyInfo)
|
||||||
return rsaEncrypt(Buffer.from(JSON.stringify({
|
return rsaEncrypt(Buffer.from(JSON.stringify({
|
||||||
clientId: keyInfo.clientId,
|
clientId: keyInfo.clientId,
|
||||||
key: keyInfo.key,
|
key: keyInfo.key,
|
||||||
|
@ -66,7 +73,6 @@ export const authCode = async(req: http.IncomingMessage, res: http.ServerRespons
|
||||||
let code = 401
|
let code = 401
|
||||||
let msg: string = SYNC_CODE.msgAuthFailed
|
let msg: string = SYNC_CODE.msgAuthFailed
|
||||||
|
|
||||||
// console.log(req.headers)
|
|
||||||
let ip = getAvailableIP(req)
|
let ip = getAvailableIP(req)
|
||||||
if (ip) {
|
if (ip) {
|
||||||
if (typeof req.headers.m == 'string' && req.headers.m) {
|
if (typeof req.headers.m == 'string' && req.headers.m) {
|
||||||
|
@ -89,13 +95,15 @@ export const authCode = async(req: http.IncomingMessage, res: http.ServerRespons
|
||||||
code = 403
|
code = 403
|
||||||
msg = SYNC_CODE.msgBlockedIp
|
msg = SYNC_CODE.msgBlockedIp
|
||||||
}
|
}
|
||||||
|
// console.log(req.headers)
|
||||||
|
|
||||||
res.writeHead(code)
|
res.writeHead(code)
|
||||||
res.end(msg)
|
res.end(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
const verifyConnection = (encryptMsg: string, userId: string) => {
|
const verifyConnection = (encryptMsg: string, userId: string) => {
|
||||||
const keyInfo = getClientKeyInfo(userId)
|
const userSpace = getUserSpace()
|
||||||
|
const keyInfo = userSpace.dataManage.getClientKeyInfo(userId)
|
||||||
if (!keyInfo) return false
|
if (!keyInfo) return false
|
||||||
let text
|
let text
|
||||||
try {
|
try {
|
|
@ -0,0 +1,14 @@
|
||||||
|
import {
|
||||||
|
startServer,
|
||||||
|
stopServer,
|
||||||
|
getStatus,
|
||||||
|
generateCode,
|
||||||
|
} from './server'
|
||||||
|
|
||||||
|
|
||||||
|
export {
|
||||||
|
startServer,
|
||||||
|
stopServer,
|
||||||
|
getStatus,
|
||||||
|
generateCode,
|
||||||
|
}
|
|
@ -1,15 +1,16 @@
|
||||||
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 { modules, callObj } from './modules'
|
import { modules, callObj } from '../modules'
|
||||||
import { authCode, authConnect } from './auth'
|
import { authCode, authConnect } from './auth'
|
||||||
import log from '../log'
|
import { getAddress } from '../../utils'
|
||||||
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@common/constants'
|
import { SYNC_CLOSE_CODE, SYNC_CODE } from '../../constants'
|
||||||
import { decryptMsg, encryptMsg, generateCode as handleGenerateCode } from './utils'
|
import { getUserSpace, releaseUserSpace, getServerId, initServerInfo } from '../user'
|
||||||
import { getAddress } from '../utils'
|
|
||||||
import { sendServerStatus } from '@main/modules/winMain/index'
|
|
||||||
import { getClientKeyInfo, getServerId, saveClientKeyInfo } from '../data'
|
|
||||||
import { createMsg2call } from 'message2call'
|
import { createMsg2call } from 'message2call'
|
||||||
|
import log from '../../log'
|
||||||
|
import { sendServerStatus } from '@main/modules/winMain'
|
||||||
|
import { decryptMsg, encryptMsg, generateCode as handleGenerateCode } from '../utils/tools'
|
||||||
|
import migrateData from '../../migrate'
|
||||||
|
|
||||||
|
|
||||||
let status: LX.Sync.ServerStatus = {
|
let status: LX.Sync.ServerStatus = {
|
||||||
|
@ -19,6 +20,7 @@ let status: LX.Sync.ServerStatus = {
|
||||||
code: '',
|
code: '',
|
||||||
devices: [],
|
devices: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
let stopingServer = false
|
let stopingServer = false
|
||||||
|
|
||||||
const codeTools: {
|
const codeTools: {
|
||||||
|
@ -51,6 +53,7 @@ const syncData = async(socket: LX.Sync.Server.Socket) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const registerLocalSyncEvent = async(wss: LX.Sync.Server.SocketServer) => {
|
const registerLocalSyncEvent = async(wss: LX.Sync.Server.SocketServer) => {
|
||||||
for (const module of Object.values(modules)) {
|
for (const module of Object.values(modules)) {
|
||||||
module.registerEvent(wss)
|
module.registerEvent(wss)
|
||||||
|
@ -63,10 +66,11 @@ const unregisterLocalSyncEvent = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
console.log('duplicate client', client.keyInfo.deviceName)
|
log.info('duplicate client', client.userInfo.name, client.keyInfo.deviceName)
|
||||||
client.isReady = false
|
client.isReady = false
|
||||||
client.close(SYNC_CLOSE_CODE.normal)
|
client.close(SYNC_CLOSE_CODE.normal)
|
||||||
}
|
}
|
||||||
|
@ -76,15 +80,18 @@ const handleConnection = async(socket: LX.Sync.Server.Socket, request: IncomingM
|
||||||
const queryData = url.parse(request.url as string, true).query as Record<string, string>
|
const queryData = url.parse(request.url as string, true).query as Record<string, string>
|
||||||
|
|
||||||
// // if (typeof socket.handshake.query.i != 'string') return socket.disconnect(true)
|
// // if (typeof socket.handshake.query.i != 'string') return socket.disconnect(true)
|
||||||
const keyInfo = getClientKeyInfo(queryData.i)
|
const userSpace = getUserSpace()
|
||||||
|
const keyInfo = userSpace.dataManage.getClientKeyInfo(queryData.i)
|
||||||
if (!keyInfo) {
|
if (!keyInfo) {
|
||||||
socket.close(SYNC_CLOSE_CODE.failed)
|
socket.close(SYNC_CLOSE_CODE.failed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
keyInfo.lastSyncDate = Date.now()
|
keyInfo.lastConnectDate = Date.now()
|
||||||
saveClientKeyInfo(keyInfo)
|
userSpace.dataManage.saveClientKeyInfo(keyInfo)
|
||||||
// // socket.lx_keyInfo = keyInfo
|
// // socket.lx_keyInfo = keyInfo
|
||||||
socket.keyInfo = keyInfo
|
socket.keyInfo = keyInfo
|
||||||
|
socket.userInfo = { name: 'default' }
|
||||||
|
|
||||||
checkDuplicateClient(socket)
|
checkDuplicateClient(socket)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -95,13 +102,12 @@ const handleConnection = async(socket: LX.Sync.Server.Socket, request: IncomingM
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
status.devices.push(keyInfo)
|
status.devices.push(keyInfo)
|
||||||
socket.onClose(() => {
|
|
||||||
// console.log('disconnect', reason)
|
|
||||||
status.devices.splice(status.devices.findIndex(k => k.clientId == keyInfo?.clientId), 1)
|
|
||||||
sendServerStatus(status)
|
|
||||||
})
|
|
||||||
// handleConnection(io, socket)
|
// handleConnection(io, socket)
|
||||||
sendServerStatus(status)
|
sendServerStatus(status)
|
||||||
|
socket.onClose(() => {
|
||||||
|
status.devices.splice(status.devices.findIndex(k => k.clientId == keyInfo.clientId), 1)
|
||||||
|
sendServerStatus(status)
|
||||||
|
})
|
||||||
|
|
||||||
// console.log('connection', keyInfo.deviceName)
|
// console.log('connection', keyInfo.deviceName)
|
||||||
log.info('connection', keyInfo.deviceName)
|
log.info('connection', keyInfo.deviceName)
|
||||||
|
@ -111,8 +117,8 @@ 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)
|
releaseUserSpace()
|
||||||
}
|
}
|
||||||
|
|
||||||
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) => {
|
||||||
|
@ -229,7 +235,7 @@ const handleStartServer = async(port = 9527, ip = '0.0.0.0') => await new Promis
|
||||||
closeEvents = []
|
closeEvents = []
|
||||||
msg2call.onDestroy()
|
msg2call.onDestroy()
|
||||||
if (socket.isReady) {
|
if (socket.isReady) {
|
||||||
log.info('deconnection', socket.keyInfo.deviceName)
|
log.info('deconnection', socket.userInfo.name, socket.keyInfo.deviceName)
|
||||||
// events = {}
|
// events = {}
|
||||||
if (!status.devices.length) handleUnconnection()
|
if (!status.devices.length) handleUnconnection()
|
||||||
} else {
|
} else {
|
||||||
|
@ -247,14 +253,16 @@ const handleStartServer = async(port = 9527, ip = '0.0.0.0') => await new Promis
|
||||||
if (!wss) return
|
if (!wss) return
|
||||||
for (const client of wss.clients) handler(client)
|
for (const client of wss.clients) handler(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleConnection(socket, request)
|
void handleConnection(socket, request)
|
||||||
})
|
})
|
||||||
|
|
||||||
httpServer.on('upgrade', function upgrade(request, socket, head) {
|
httpServer.on('upgrade', function upgrade(request, socket, head) {
|
||||||
socket.on('error', onSocketError)
|
socket.addListener('error', onSocketError)
|
||||||
// This function is not defined on purpose. Implement it with your own logic.
|
// This function is not defined on purpose. Implement it with your own logic.
|
||||||
authConnection(request, err => {
|
authConnection(request, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
console.log(err)
|
||||||
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n')
|
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n')
|
||||||
socket.destroy()
|
socket.destroy()
|
||||||
return
|
return
|
||||||
|
@ -270,6 +278,7 @@ const handleStartServer = async(port = 9527, ip = '0.0.0.0') => await new Promis
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
wss?.clients.forEach(socket => {
|
wss?.clients.forEach(socket => {
|
||||||
if (socket.isAlive == false) {
|
if (socket.isAlive == false) {
|
||||||
|
log.info('alive check false:', socket.userInfo.name, socket.keyInfo.deviceName)
|
||||||
socket.terminate()
|
socket.terminate()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -321,7 +330,6 @@ const handleStopServer = async() => new Promise<void>((resolve, reject) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
export const stopServer = async() => {
|
export const stopServer = async() => {
|
||||||
console.log('stop')
|
|
||||||
codeTools.stop()
|
codeTools.stop()
|
||||||
if (!status.status) {
|
if (!status.status) {
|
||||||
status.status = false
|
status.status = false
|
||||||
|
@ -345,16 +353,20 @@ export const stopServer = async() => {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
status.message = err.message
|
status.message = err.message
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
stopingServer = false
|
|
||||||
sendServerStatus(status)
|
sendServerStatus(status)
|
||||||
|
stopingServer = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const startServer = async(port: number) => {
|
export const startServer = async(port: number) => {
|
||||||
console.log('status.status', status.status)
|
// if (status.status) await handleStopServer()
|
||||||
|
console.log('status.status', status.status, stopingServer)
|
||||||
if (stopingServer) return
|
if (stopingServer) return
|
||||||
if (status.status) await handleStopServer()
|
if (status.status) await handleStopServer()
|
||||||
|
|
||||||
|
await migrateData(global.lxDataPath)
|
||||||
|
await initServerInfo()
|
||||||
|
|
||||||
log.info('starting sync server')
|
log.info('starting sync server')
|
||||||
await handleStartServer(port).then(() => {
|
await handleStartServer(port).then(() => {
|
||||||
console.log('sync server started')
|
console.log('sync server started')
|
|
@ -0,0 +1,142 @@
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { randomBytes } from 'node:crypto'
|
||||||
|
import { throttle } from '@common/utils/common'
|
||||||
|
import { filterFileName, toMD5 } from '../utils'
|
||||||
|
import { File } from '../../constants'
|
||||||
|
|
||||||
|
|
||||||
|
interface ServerInfo {
|
||||||
|
serverId: string
|
||||||
|
version: number
|
||||||
|
}
|
||||||
|
interface DevicesInfo {
|
||||||
|
userName: string
|
||||||
|
clients: Record<string, LX.Sync.ServerKeyInfo>
|
||||||
|
}
|
||||||
|
const saveServerInfoThrottle = throttle(() => {
|
||||||
|
fs.writeFile(path.join(global.lxDataPath, File.serverDataPath, File.serverInfoJSON), JSON.stringify(serverInfo), (err) => {
|
||||||
|
if (err) console.error(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
let serverInfo: ServerInfo
|
||||||
|
const exists = async(path: string) => fs.promises.stat(path).then(() => true).catch(() => false)
|
||||||
|
export const initServerInfo = async() => {
|
||||||
|
if (serverInfo != null) return
|
||||||
|
const serverInfoFilePath = path.join(global.lxDataPath, File.serverDataPath, File.serverInfoJSON)
|
||||||
|
if (await exists(serverInfoFilePath)) {
|
||||||
|
serverInfo = JSON.parse((await fs.promises.readFile(serverInfoFilePath)).toString())
|
||||||
|
} else {
|
||||||
|
serverInfo = {
|
||||||
|
serverId: randomBytes(4 * 4).toString('base64'),
|
||||||
|
version: 2,
|
||||||
|
}
|
||||||
|
const syncDataPath = path.join(global.lxDataPath, File.serverDataPath)
|
||||||
|
if (!await exists(syncDataPath)) {
|
||||||
|
await fs.promises.mkdir(syncDataPath, { recursive: true })
|
||||||
|
}
|
||||||
|
saveServerInfoThrottle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const getServerId = () => {
|
||||||
|
return serverInfo.serverId
|
||||||
|
}
|
||||||
|
export const getVersion = async() => {
|
||||||
|
await initServerInfo()
|
||||||
|
return serverInfo.version ?? 1
|
||||||
|
}
|
||||||
|
export const setVersion = async(version: number) => {
|
||||||
|
await initServerInfo()
|
||||||
|
serverInfo.version = version
|
||||||
|
saveServerInfoThrottle()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getUserDirname = (userName: string) => `${filterFileName(userName)}_${toMD5(userName).substring(0, 6)}`
|
||||||
|
|
||||||
|
export const getUserConfig = (userName: string) => {
|
||||||
|
return {
|
||||||
|
maxSnapshotNum: global.lx.appSetting['sync.server.maxSsnapshotNum'],
|
||||||
|
'list.addMusicLocationType': global.lx.appSetting['list.addMusicLocationType'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 读取所有用户目录下的devicesInfo信息,建立clientId与用户的对应关系,用于非首次连接
|
||||||
|
// let deviceUserMap: Map<string, string> = new Map<string, string>()
|
||||||
|
// const init
|
||||||
|
// for (const deviceInfo of fs.readdirSync(syncDataPath).map(dirname => {
|
||||||
|
// const devicesFilePath = path.join(syncDataPath, dirname, File.userDevicesJSON)
|
||||||
|
// if (fs.existsSync(devicesFilePath)) {
|
||||||
|
// const devicesInfo = JSON.parse(fs.readFileSync(devicesFilePath).toString()) as DevicesInfo
|
||||||
|
// if (getUserDirname(devicesInfo.userName) == dirname) return { userName: devicesInfo.userName, devices: devicesInfo.clients }
|
||||||
|
// }
|
||||||
|
// return { userName: '', devices: {} }
|
||||||
|
// })) {
|
||||||
|
// for (const device of Object.values(deviceInfo.devices)) {
|
||||||
|
// if (deviceInfo.userName) deviceUserMap.set(device.clientId, deviceInfo.userName)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// export const getUserName = (clientId: string): string | null => {
|
||||||
|
// if (!clientId) return null
|
||||||
|
// return deviceUserMap.get(clientId) ?? null
|
||||||
|
// }
|
||||||
|
// export const setUserName = (clientId: string, dir: string) => {
|
||||||
|
// deviceUserMap.set(clientId, dir)
|
||||||
|
// }
|
||||||
|
// export const deleteUserName = (clientId: string) => {
|
||||||
|
// deviceUserMap.delete(clientId)
|
||||||
|
// }
|
||||||
|
|
||||||
|
export const createClientKeyInfo = (deviceName: string, isMobile: boolean): LX.Sync.ServerKeyInfo => {
|
||||||
|
const keyInfo: LX.Sync.ServerKeyInfo = {
|
||||||
|
clientId: randomBytes(4 * 4).toString('base64'),
|
||||||
|
key: randomBytes(16).toString('base64'),
|
||||||
|
deviceName,
|
||||||
|
isMobile,
|
||||||
|
lastConnectDate: 0,
|
||||||
|
}
|
||||||
|
return keyInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UserDataManage {
|
||||||
|
userName: string
|
||||||
|
userDir: string
|
||||||
|
devicesFilePath: string
|
||||||
|
devicesInfo: DevicesInfo
|
||||||
|
private readonly saveDevicesInfoThrottle: () => void
|
||||||
|
|
||||||
|
saveClientKeyInfo = (keyInfo: LX.Sync.ServerKeyInfo) => {
|
||||||
|
if (this.devicesInfo.clients[keyInfo.clientId] == null && Object.keys(this.devicesInfo.clients).length > 101) throw new Error('max keys')
|
||||||
|
this.devicesInfo.clients[keyInfo.clientId] = keyInfo
|
||||||
|
this.saveDevicesInfoThrottle()
|
||||||
|
}
|
||||||
|
|
||||||
|
getClientKeyInfo = (clientId?: string): LX.Sync.ServerKeyInfo | null => {
|
||||||
|
if (!clientId) return null
|
||||||
|
return this.devicesInfo.clients[clientId] ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
isIncluedsClient = (clientId: string) => {
|
||||||
|
return Object.values(this.devicesInfo.clients).some(client => client.clientId == clientId)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(userName: string) {
|
||||||
|
this.userName = userName
|
||||||
|
const syncDataPath = path.join(global.lxDataPath, File.serverDataPath)
|
||||||
|
this.userDir = syncDataPath
|
||||||
|
this.devicesFilePath = path.join(this.userDir, File.userDevicesJSON)
|
||||||
|
this.devicesInfo = fs.existsSync(this.devicesFilePath) ? JSON.parse(fs.readFileSync(this.devicesFilePath).toString()) : { userName, clients: {} }
|
||||||
|
|
||||||
|
this.saveDevicesInfoThrottle = throttle(() => {
|
||||||
|
fs.writeFile(this.devicesFilePath, JSON.stringify(this.devicesInfo), 'utf8', (err) => {
|
||||||
|
if (err) console.error(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// type UserDataManages = Map<string, UserDataManage>
|
||||||
|
|
||||||
|
// export const createUserDataManage = (user: LX.UserConfig) => {
|
||||||
|
// const manage = Object.create(userDataManage) as typeof userDataManage
|
||||||
|
// manage.userDir = user.dataPath
|
||||||
|
// }
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { UserDataManage } from './data'
|
||||||
|
import {
|
||||||
|
ListManage,
|
||||||
|
} from '../modules'
|
||||||
|
|
||||||
|
export interface UserSpace {
|
||||||
|
dataManage: UserDataManage
|
||||||
|
listManage: ListManage
|
||||||
|
}
|
||||||
|
const users = new Map<string, UserSpace>()
|
||||||
|
|
||||||
|
const delayTime = 10 * 1000
|
||||||
|
const delayReleaseTimeouts = new Map<string, NodeJS.Timeout>()
|
||||||
|
const clearDelayReleaseTimeout = (userName: string) => {
|
||||||
|
if (!delayReleaseTimeouts.has(userName)) return
|
||||||
|
|
||||||
|
clearTimeout(delayReleaseTimeouts.get(userName))
|
||||||
|
delayReleaseTimeouts.delete(userName)
|
||||||
|
}
|
||||||
|
const seartDelayReleaseTimeout = (userName: string) => {
|
||||||
|
clearDelayReleaseTimeout(userName)
|
||||||
|
delayReleaseTimeouts.set(userName, setTimeout(() => {
|
||||||
|
users.delete(userName)
|
||||||
|
}, delayTime))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getUserSpace = (userName = 'default') => {
|
||||||
|
clearDelayReleaseTimeout(userName)
|
||||||
|
|
||||||
|
let user = users.get(userName)
|
||||||
|
if (!user) {
|
||||||
|
console.log('new user data manage:', userName)
|
||||||
|
const dataManage = new UserDataManage(userName)
|
||||||
|
users.set(userName, user = {
|
||||||
|
dataManage,
|
||||||
|
listManage: new ListManage(dataManage),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
export const releaseUserSpace = (userName = 'default', force = false) => {
|
||||||
|
if (force) {
|
||||||
|
clearDelayReleaseTimeout(userName)
|
||||||
|
users.delete(userName)
|
||||||
|
} else seartDelayReleaseTimeout(userName)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export * from './data'
|
|
@ -1,61 +0,0 @@
|
||||||
import { toMD5 } from '@common/utils/nodejs'
|
|
||||||
import type http from 'node:http'
|
|
||||||
import {
|
|
||||||
getSnapshotInfo,
|
|
||||||
saveSnapshot,
|
|
||||||
saveSnapshotInfo,
|
|
||||||
type SnapshotInfo,
|
|
||||||
} from '../data'
|
|
||||||
import { decodeData, encodeData, getLocalListData } from '../utils'
|
|
||||||
|
|
||||||
export const generateCode = (): string => {
|
|
||||||
return Math.random().toString().substring(2, 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getIP = (request: http.IncomingMessage) => {
|
|
||||||
return request.socket.remoteAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
export const encryptMsg = async(keyInfo: LX.Sync.ServerKeyInfo | null, msg: string): Promise<string> => {
|
|
||||||
return encodeData(msg)
|
|
||||||
// console.log('enmsg raw: ', msg.length, 'en: ', len.length)
|
|
||||||
// return len
|
|
||||||
// if (!keyInfo) return ''
|
|
||||||
// return aesEncrypt(msg, keyInfo.key, keyInfo.iv)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const decryptMsg = async(keyInfo: LX.Sync.ServerKeyInfo | null, enMsg: string): Promise<string> => {
|
|
||||||
return decodeData(enMsg)
|
|
||||||
// console.log('decmsg raw: ', len.length, 'en: ', enMsg.length)
|
|
||||||
// return len
|
|
||||||
// if (!keyInfo) return ''
|
|
||||||
// let msg = ''
|
|
||||||
// try {
|
|
||||||
// msg = aesDecrypt(enMsg, keyInfo.key, keyInfo.iv)
|
|
||||||
// } catch (err) {
|
|
||||||
// console.log(err)
|
|
||||||
// }
|
|
||||||
// return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
let snapshotInfo: SnapshotInfo
|
|
||||||
export const createSnapshot = async() => {
|
|
||||||
if (!snapshotInfo) snapshotInfo = getSnapshotInfo()
|
|
||||||
const listData = JSON.stringify(await getLocalListData())
|
|
||||||
const md5 = toMD5(listData)
|
|
||||||
if (snapshotInfo.latest == md5) return md5
|
|
||||||
if (snapshotInfo.list.includes(md5)) {
|
|
||||||
snapshotInfo.list.splice(snapshotInfo.list.indexOf(md5), 1)
|
|
||||||
} else await saveSnapshot(md5, listData)
|
|
||||||
if (snapshotInfo.latest) snapshotInfo.list.unshift(snapshotInfo.latest)
|
|
||||||
snapshotInfo.latest = md5
|
|
||||||
snapshotInfo.time = Date.now()
|
|
||||||
saveSnapshotInfo(snapshotInfo)
|
|
||||||
return md5
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const getCurrentListInfoKey = async() => {
|
|
||||||
// if (!snapshotInfo) snapshotInfo = getSnapshotInfo()
|
|
||||||
return createSnapshot()
|
|
||||||
}
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import crypto from 'node:crypto'
|
||||||
|
|
||||||
|
|
||||||
|
export const createDirSync = (path: string) => {
|
||||||
|
if (!fs.existsSync(path)) {
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(path, { recursive: true })
|
||||||
|
} catch (e: any) {
|
||||||
|
if (e.code !== 'EEXIST') {
|
||||||
|
console.error('Could not set up log directory, error was: ', e)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileNameRxp = /[\\/:*?#"<>|]/g
|
||||||
|
export const filterFileName = (name: string): string => name.replace(fileNameRxp, '')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 MD5 hash
|
||||||
|
* @param {*} str
|
||||||
|
*/
|
||||||
|
export const toMD5 = (str: string) => crypto.createHash('md5').update(str).digest('hex')
|
||||||
|
|
||||||
|
export const checkAndCreateDirSync = (path: string) => {
|
||||||
|
if (!fs.existsSync(path)) {
|
||||||
|
fs.mkdirSync(path, { recursive: true })
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { networkInterfaces } from 'node:os'
|
||||||
|
import { createCipheriv, createDecipheriv, publicEncrypt, privateDecrypt, constants } from 'node:crypto'
|
||||||
|
// import { join } from 'node:path'
|
||||||
|
import zlib from 'node:zlib'
|
||||||
|
import type http from 'node:http'
|
||||||
|
// import getStore from '@/utils/store'
|
||||||
|
// import syncLog from '../../log'
|
||||||
|
// import { getUserName } from '../user/data'
|
||||||
|
// import { saveClientKeyInfo } from './data'
|
||||||
|
|
||||||
|
export const getAddress = (): string[] => {
|
||||||
|
const nets = networkInterfaces()
|
||||||
|
const results: string[] = []
|
||||||
|
// console.log(nets)
|
||||||
|
|
||||||
|
for (const interfaceInfos of Object.values(nets)) {
|
||||||
|
if (!interfaceInfos) continue
|
||||||
|
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
|
||||||
|
for (const interfaceInfo of interfaceInfos) {
|
||||||
|
if (interfaceInfo.family === 'IPv4' && !interfaceInfo.internal) {
|
||||||
|
results.push(interfaceInfo.address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generateCode = (): string => {
|
||||||
|
return Math.random().toString().substring(2, 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getIP = (request: http.IncomingMessage) => {
|
||||||
|
return request.socket.remoteAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const aesEncrypt = (buffer: string | Buffer, key: string): string => {
|
||||||
|
const cipher = createCipheriv('aes-128-ecb', Buffer.from(key, 'base64'), '')
|
||||||
|
return Buffer.concat([cipher.update(buffer), cipher.final()]).toString('base64')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const aesDecrypt = (text: string, key: string): string => {
|
||||||
|
const decipher = createDecipheriv('aes-128-ecb', Buffer.from(key, 'base64'), '')
|
||||||
|
return Buffer.concat([decipher.update(Buffer.from(text, 'base64')), decipher.final()]).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const rsaEncrypt = (buffer: Buffer, key: string): string => {
|
||||||
|
return publicEncrypt({ key, padding: constants.RSA_PKCS1_OAEP_PADDING }, buffer).toString('base64')
|
||||||
|
}
|
||||||
|
export const rsaDecrypt = (buffer: Buffer, key: string): Buffer => {
|
||||||
|
return privateDecrypt({ key, padding: constants.RSA_PKCS1_OAEP_PADDING }, buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 encryptMsg = async(keyInfo: LX.Sync.ServerKeyInfo | null, msg: string): Promise<string> => {
|
||||||
|
return msg.length > 1024
|
||||||
|
? 'cg_' + await gzip(msg)
|
||||||
|
: msg
|
||||||
|
// if (!keyInfo) return ''
|
||||||
|
// return aesEncrypt(msg, keyInfo.key, keyInfo.iv)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const decryptMsg = async(keyInfo: LX.Sync.ServerKeyInfo | null, enMsg: string): Promise<string> => {
|
||||||
|
return enMsg.substring(0, 3) == 'cg_'
|
||||||
|
? await unGzip(enMsg.replace('cg_', ''))
|
||||||
|
: enMsg
|
||||||
|
// console.log('decmsg raw: ', len.length, 'en: ', enMsg.length)
|
||||||
|
|
||||||
|
// if (!keyInfo) return ''
|
||||||
|
// let msg = ''
|
||||||
|
// try {
|
||||||
|
// msg = aesDecrypt(enMsg, keyInfo.key, keyInfo.iv)
|
||||||
|
// } catch (err) {
|
||||||
|
// console.log(err)
|
||||||
|
// }
|
||||||
|
// return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// export const getSnapshotFilePath = (keyInfo: LX.Sync.KeyInfo): string => {
|
||||||
|
// return join(global.lx.snapshotPath, `snapshot_${keyInfo.snapshotKey}.json`)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export const sendStatus = (status: LX.Sync.ServerStatus) => {
|
||||||
|
// syncLog.info('status', status.devices.map(d => `${getUserName(d.clientId) ?? ''} ${d.deviceName}`))
|
||||||
|
// }
|
||||||
|
|
|
@ -193,40 +193,40 @@ export const handleRemoteListAction = async({ action, data }: LX.Sync.ActionList
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'list_data_overwrite':
|
case 'list_data_overwrite':
|
||||||
void global.lx.event_list.list_data_overwrite(data, true)
|
await global.lx.event_list.list_data_overwrite(data, true)
|
||||||
break
|
break
|
||||||
case 'list_create':
|
case 'list_create':
|
||||||
void global.lx.event_list.list_create(data.position, data.listInfos, true)
|
await global.lx.event_list.list_create(data.position, data.listInfos, true)
|
||||||
break
|
break
|
||||||
case 'list_remove':
|
case 'list_remove':
|
||||||
void global.lx.event_list.list_remove(data, true)
|
await global.lx.event_list.list_remove(data, true)
|
||||||
break
|
break
|
||||||
case 'list_update':
|
case 'list_update':
|
||||||
void global.lx.event_list.list_update(data, true)
|
await global.lx.event_list.list_update(data, true)
|
||||||
break
|
break
|
||||||
case 'list_update_position':
|
case 'list_update_position':
|
||||||
void global.lx.event_list.list_update_position(data.position, data.ids, true)
|
await global.lx.event_list.list_update_position(data.position, data.ids, true)
|
||||||
break
|
break
|
||||||
case 'list_music_add':
|
case 'list_music_add':
|
||||||
void global.lx.event_list.list_music_add(data.id, data.musicInfos, data.addMusicLocationType, true)
|
await global.lx.event_list.list_music_add(data.id, data.musicInfos, data.addMusicLocationType, true)
|
||||||
break
|
break
|
||||||
case 'list_music_move':
|
case 'list_music_move':
|
||||||
void global.lx.event_list.list_music_move(data.fromId, data.toId, data.musicInfos, data.addMusicLocationType, true)
|
await global.lx.event_list.list_music_move(data.fromId, data.toId, data.musicInfos, data.addMusicLocationType, true)
|
||||||
break
|
break
|
||||||
case 'list_music_remove':
|
case 'list_music_remove':
|
||||||
void global.lx.event_list.list_music_remove(data.listId, data.ids, true)
|
await global.lx.event_list.list_music_remove(data.listId, data.ids, true)
|
||||||
break
|
break
|
||||||
case 'list_music_update':
|
case 'list_music_update':
|
||||||
void global.lx.event_list.list_music_update(data, true)
|
await global.lx.event_list.list_music_update(data, true)
|
||||||
break
|
break
|
||||||
case 'list_music_update_position':
|
case 'list_music_update_position':
|
||||||
void global.lx.event_list.list_music_update_position(data.listId, data.position, data.ids, true)
|
await global.lx.event_list.list_music_update_position(data.listId, data.position, data.ids, true)
|
||||||
break
|
break
|
||||||
case 'list_music_overwrite':
|
case 'list_music_overwrite':
|
||||||
void global.lx.event_list.list_music_overwrite(data.listId, data.musicInfos, true)
|
await global.lx.event_list.list_music_overwrite(data.listId, data.musicInfos, true)
|
||||||
break
|
break
|
||||||
case 'list_music_clear':
|
case 'list_music_clear':
|
||||||
void global.lx.event_list.list_music_clear(data, true)
|
await global.lx.event_list.list_music_clear(data, true)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -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 | null) => void) | null = null
|
let selectModeListenr: ((mode: LX.Sync.ListSyncMode | 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 }) => {
|
||||||
|
@ -46,7 +46,7 @@ export const sendServerStatus = (status: LX.Sync.ServerStatus) => {
|
||||||
data: status,
|
data: status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export const sendSelectMode = (deviceName: string, listener: (mode: LX.Sync.Mode | null) => void) => {
|
export const sendSelectMode = (deviceName: string, listener: (mode: LX.Sync.ListSyncMode | null) => void) => {
|
||||||
selectModeListenr = listener
|
selectModeListenr = listener
|
||||||
sendSyncAction({ action: 'select_mode', data: deviceName })
|
sendSyncAction({ action: 'select_mode', data: deviceName })
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ declare global {
|
||||||
interface Socket extends WS.WebSocket {
|
interface Socket extends WS.WebSocket {
|
||||||
isAlive?: boolean
|
isAlive?: boolean
|
||||||
isReady: boolean
|
isReady: boolean
|
||||||
|
userInfo: { name: 'default' }
|
||||||
keyInfo: ServerKeyInfo
|
keyInfo: ServerKeyInfo
|
||||||
onClose: (handler: (err: Error) => (void | Promise<void>)) => () => void
|
onClose: (handler: (err: Error) => (void | Promise<void>)) => () => void
|
||||||
broadcast: (handler: (client: Socket) => void) => void
|
broadcast: (handler: (client: Socket) => void) => void
|
||||||
|
|
|
@ -44,6 +44,8 @@ export default {
|
||||||
host: appSetting['sync.client.host'],
|
host: appSetting['sync.client.host'],
|
||||||
authCode: code,
|
authCode: code,
|
||||||
},
|
},
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -46,6 +46,8 @@ export default () => {
|
||||||
enable: appSetting['sync.enable'],
|
enable: appSetting['sync.enable'],
|
||||||
port: appSetting['sync.server.port'],
|
port: appSetting['sync.server.port'],
|
||||||
},
|
},
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -57,6 +59,8 @@ export default () => {
|
||||||
enable: appSetting['sync.enable'],
|
enable: appSetting['sync.enable'],
|
||||||
host: appSetting['sync.client.host'],
|
host: appSetting['sync.client.host'],
|
||||||
},
|
},
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
|
@ -56,6 +56,8 @@ export default () => {
|
||||||
enable: appSetting['sync.enable'],
|
enable: appSetting['sync.enable'],
|
||||||
port: appSetting['sync.server.port'],
|
port: appSetting['sync.server.port'],
|
||||||
},
|
},
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -67,6 +69,8 @@ export default () => {
|
||||||
enable: appSetting['sync.enable'],
|
enable: appSetting['sync.enable'],
|
||||||
host: appSetting['sync.client.host'],
|
host: appSetting['sync.client.host'],
|
||||||
},
|
},
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
Loading…
Reference in New Issue