重构同步服务端功能部分代码,使其更易扩展新功能
parent
70815e276e
commit
0d76c9ccb4
|
@ -31,3 +31,4 @@
|
|||
### 其他
|
||||
|
||||
- 更新 electron 到 v22.3.21
|
||||
- 重构同步服务端功能部分代码,使其更易扩展新功能
|
||||
|
|
|
@ -140,5 +140,18 @@ declare namespace LX {
|
|||
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<'server_status', ServerStatus>
|
||||
|
||||
type SyncServiceActions = SyncAction<'select_mode', Mode>
|
||||
type SyncServiceActions = SyncAction<'select_mode', ListSyncMode>
|
||||
| SyncAction<'get_server_status'>
|
||||
| SyncAction<'get_client_status'>
|
||||
| SyncAction<'generate_code'>
|
||||
|
@ -90,14 +90,17 @@ declare namespace LX {
|
|||
clientId: string
|
||||
key: string
|
||||
deviceName: string
|
||||
lastSyncDate?: number
|
||||
snapshotKey: string
|
||||
lastConnectDate?: number
|
||||
isMobile: boolean
|
||||
}
|
||||
|
||||
interface ListInfo {
|
||||
lastSyncDate?: number
|
||||
snapshotKey: string
|
||||
}
|
||||
type ListData = Omit<LX.List.ListDataFull, 'tempList'>
|
||||
|
||||
type Mode = 'merge_local_remote'
|
||||
type ListSyncMode = 'merge_local_remote'
|
||||
| 'merge_remote_local'
|
||||
| 'overwrite_local_remote'
|
||||
| 'overwrite_remote_local'
|
||||
|
|
|
@ -1,26 +1,13 @@
|
|||
declare namespace LX {
|
||||
namespace Sync {
|
||||
type ActionList = SyncAction<'list_data_overwrite', LX.List.ListActionDataOverwrite>
|
||||
| SyncAction<'list_create', LX.List.ListActionAdd>
|
||||
| SyncAction<'list_remove', LX.List.ListActionRemove>
|
||||
| SyncAction<'list_update', LX.List.ListActionUpdate>
|
||||
| SyncAction<'list_update_position', LX.List.ListActionUpdatePosition>
|
||||
| SyncAction<'list_music_add', LX.List.ListActionMusicAdd>
|
||||
| SyncAction<'list_music_move', LX.List.ListActionMusicMove>
|
||||
| SyncAction<'list_music_remove', LX.List.ListActionMusicRemove>
|
||||
| SyncAction<'list_music_update', LX.List.ListActionMusicUpdate>
|
||||
| SyncAction<'list_music_update_position', LX.List.ListActionMusicUpdatePosition>
|
||||
| SyncAction<'list_music_overwrite', LX.List.ListActionMusicOverwrite>
|
||||
| SyncAction<'list_music_clear', LX.List.ListActionMusicClear>
|
||||
|
||||
type ServerActions = WarpPromiseRecord<{
|
||||
onListSyncAction: (action: LX.Sync.ActionList) => void
|
||||
onListSyncAction: (action: LX.List.ActionList) => void
|
||||
}>
|
||||
|
||||
type ClientActions = WarpPromiseRecord<{
|
||||
onListSyncAction: (action: LX.Sync.ActionList) => void
|
||||
onListSyncAction: (action: LX.List.ActionList) => void
|
||||
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_set_list_data: (data: ListData) => void
|
||||
list_sync_finished: () => void
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { request, generateRsaKey } from './utils'
|
||||
import { getSyncAuthKey, setSyncAuthKey } from '../data'
|
||||
import { getSyncAuthKey, setSyncAuthKey } from './data'
|
||||
import { SYNC_CODE } from '@common/constants'
|
||||
import log from '../log'
|
||||
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 log from '../log'
|
||||
import { parseUrl } from './utils'
|
||||
import migrateData from '../migrate'
|
||||
|
||||
let connectId = 0
|
||||
|
||||
|
@ -29,6 +30,8 @@ const connectServer = async(host: string, authCode?: string) => {
|
|||
message: SYNC_CODE.connecting,
|
||||
})
|
||||
const id = connectId
|
||||
await migrateData(global.lxDataPath)
|
||||
|
||||
return handleConnect(host, authCode).catch(async err => {
|
||||
if (id != connectId) return
|
||||
sendSyncStatus({
|
||||
|
|
|
@ -21,7 +21,7 @@ export const list_sync_get_md5 = async(socket: LX.Sync.Client.Socket) => {
|
|||
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) => {
|
||||
sendCloseSelectMode()
|
||||
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 {
|
||||
startServer,
|
||||
stopServer,
|
||||
|
@ -12,5 +11,4 @@ export {
|
|||
stopServer,
|
||||
getStatus,
|
||||
generateCode,
|
||||
modules,
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import * as list from './list'
|
||||
// export * as theme from './theme'
|
||||
|
||||
|
||||
export const callObj = Object.assign({}, list.handler)
|
||||
import { sync } from './list'
|
||||
|
||||
export const callObj = Object.assign({},
|
||||
sync.handler,
|
||||
)
|
||||
|
||||
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 { default as sync } from './sync'
|
||||
export * 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 { getCurrentListInfoKey } from '../../utils'
|
||||
import { registerListActionEvent } from '../../../../utils'
|
||||
import { getUserSpace } from '../../../user'
|
||||
|
||||
// let socket: LX.Sync.Server.Socket | 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) => {
|
||||
// console.log('sendListAction', action.action)
|
||||
const key = await getCurrentListInfoKey()
|
||||
const userSpace = getUserSpace()
|
||||
const key = await userSpace.listManage.createSnapshot()
|
||||
for (const client of wss.clients) {
|
||||
if (!client.isReady) return
|
||||
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 { removeSelectModeListener, sendCloseSelectMode, sendSelectMode } from '@main/modules/winMain'
|
||||
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()
|
||||
}
|
||||
// import { LIST_IDS } from '@common/constants'
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
const getSyncMode = async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.Mode> => new Promise((resolve, reject) => {
|
||||
const handleDisconnect = (err: Error) => {
|
||||
sendCloseSelectMode()
|
||||
removeSelectModeListener()
|
||||
reject(err)
|
||||
}
|
||||
let removeEventClose = socket.onClose(handleDisconnect)
|
||||
sendSelectMode(socket.keyInfo.deviceName, (mode) => {
|
||||
if (mode == null) {
|
||||
reject(new Error('cancel'))
|
||||
return
|
||||
}
|
||||
resolve(mode)
|
||||
removeSelectModeListener()
|
||||
removeEventClose()
|
||||
})
|
||||
})
|
||||
// const getLocalListData = async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.ListData> => {
|
||||
// return getUserSpace(socket.userInfo.name).listManage.getListData()
|
||||
// }
|
||||
const getSyncMode = async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.ListSyncMode> => {
|
||||
return socket.remoteSyncList.list_sync_get_sync_mode()
|
||||
}
|
||||
|
||||
const finishedSync = async(socket: LX.Sync.Server.Socket) => {
|
||||
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 action = { action: 'list_data_overwrite', data: listData } as const
|
||||
const tasks: Array<Promise<void>> = []
|
||||
socket.broadcast((client) => {
|
||||
if (excludeIds.includes(client.keyInfo.clientId) || !client.isReady) return
|
||||
tasks.push(client.remoteSyncList.onListSyncAction(action).then(() => {
|
||||
updateDeviceSnapshotKey(socket.keyInfo, key)
|
||||
if (excludeIds.includes(client.keyInfo.clientId) || client.userInfo.name != socket.userInfo.name || !client.isReady) return
|
||||
tasks.push(client.remoteSyncList.onListSyncAction(action).then(async() => {
|
||||
const userSpace = getUserSpace(socket.userInfo.name)
|
||||
return userSpace.listManage.updateDeviceSnapshotKey(socket.keyInfo.clientId, key)
|
||||
}).catch(err => {
|
||||
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> => {
|
||||
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>
|
||||
|
@ -120,8 +111,8 @@ const handleMergeList = (
|
|||
}
|
||||
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 addMusicLocationType = global.lx.appSetting['list.addMusicLocationType']
|
||||
const mergeList = (socket: LX.Sync.Server.Socket, sourceListData: LX.Sync.ListData, targetListData: LX.Sync.ListData): LX.Sync.ListData => {
|
||||
const addMusicLocationType = getUserConfig(socket.userInfo.name)['list.addMusicLocationType']
|
||||
const newListData: LX.Sync.ListData = {
|
||||
defaultList: [],
|
||||
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 mode: LX.Sync.Mode = await getSyncMode(socket)
|
||||
const mode: LX.Sync.ListSyncMode = await getSyncMode(socket)
|
||||
|
||||
if (mode == 'cancel') {
|
||||
socket.close(SYNC_CLOSE_CODE.normal)
|
||||
|
@ -194,10 +185,10 @@ const handleMergeListData = async(socket: LX.Sync.Server.Socket): Promise<[LX.Sy
|
|||
let requiredUpdateRemoteListData = true
|
||||
switch (mode) {
|
||||
case 'merge_local_remote':
|
||||
listData = mergeList(localListData, remoteListData)
|
||||
listData = mergeList(socket, localListData, remoteListData)
|
||||
break
|
||||
case 'merge_remote_local':
|
||||
listData = mergeList(remoteListData, localListData)
|
||||
listData = mergeList(socket, remoteListData, localListData)
|
||||
break
|
||||
case 'overwrite_local_remote':
|
||||
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 [remoteListData, localListData] = await Promise.all([getRemoteListData(socket), getLocalListData()])
|
||||
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 (remoteListData.defaultList.length || remoteListData.loveList.length || remoteListData.userList.length) {
|
||||
const [mergedList, requiredUpdateLocalListData, requiredUpdateRemoteListData] = await handleMergeListData(socket)
|
||||
console.log('handleMergeListData', 'mergedList')
|
||||
console.log('handleMergeListData', 'mergedList', requiredUpdateLocalListData, requiredUpdateRemoteListData)
|
||||
let key
|
||||
if (requiredUpdateLocalListData) {
|
||||
key = await handleSetLocalListData(mergedList)
|
||||
await overwriteRemoteListData(socket, mergedList, key, [socket.keyInfo.clientId])
|
||||
if (!requiredUpdateRemoteListData) updateDeviceSnapshotKey(socket.keyInfo, key)
|
||||
key = await setLocalList(socket, mergedList)
|
||||
await overwriteRemoteListData(socket, mergedList, key, [clientId])
|
||||
if (!requiredUpdateRemoteListData) await userSpace.listManage.updateDeviceSnapshotKey(clientId, key)
|
||||
}
|
||||
if (requiredUpdateRemoteListData) {
|
||||
if (!key) key = await getCurrentListInfoKey()
|
||||
if (!key) key = await userSpace.listManage.getCurrentListInfoKey()
|
||||
await setRemotelList(socket, mergedList, key)
|
||||
}
|
||||
} else {
|
||||
await setRemotelList(socket, localListData, await getCurrentListInfoKey())
|
||||
await setRemotelList(socket, localListData, await userSpace.listManage.getCurrentListInfoKey())
|
||||
}
|
||||
} else {
|
||||
let key: string
|
||||
if (remoteListData.defaultList.length || remoteListData.loveList.length || remoteListData.userList.length) {
|
||||
key = await handleSetLocalListData(remoteListData)
|
||||
await overwriteRemoteListData(socket, remoteListData, key, [socket.keyInfo.clientId])
|
||||
key = await setLocalList(socket, remoteListData)
|
||||
await overwriteRemoteListData(socket, remoteListData, key, [clientId])
|
||||
}
|
||||
key ??= await getCurrentListInfoKey()
|
||||
updateDeviceSnapshotKey(socket.keyInfo, key)
|
||||
key ??= await userSpace.listManage.getCurrentListInfoKey()
|
||||
await userSpace.listManage.updateDeviceSnapshotKey(clientId, key)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -297,15 +292,17 @@ const mergeListDataFromSnapshot = (
|
|||
}
|
||||
const checkListLatest = async(socket: LX.Sync.Server.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
|
||||
if (latest && socket.keyInfo.snapshotKey != currentListInfoKey) updateDeviceSnapshotKey(socket.keyInfo, currentListInfoKey)
|
||||
if (latest && userCurrentListInfoKey != currentListInfoKey) await userSpace.listManage.updateDeviceSnapshotKey(socket.keyInfo.clientId, currentListInfoKey)
|
||||
return latest
|
||||
}
|
||||
const handleMergeListDataFromSnapshot = async(socket: LX.Sync.Server.Socket, snapshot: LX.Sync.ListData) => {
|
||||
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 newListData: LX.Sync.ListData = {
|
||||
defaultList: [],
|
||||
|
@ -361,17 +358,21 @@ const handleMergeListDataFromSnapshot = async(socket: LX.Sync.Server.Socket, sna
|
|||
})
|
||||
|
||||
newListData.userList = newUserList
|
||||
const key = await handleSetLocalListData(newListData)
|
||||
await setRemotelList(socket, newListData, key)
|
||||
const key = await setLocalList(socket, newListData)
|
||||
const err = await setRemotelList(socket, newListData, key).catch(err => err)
|
||||
await overwriteRemoteListData(socket, newListData, key, [socket.keyInfo.clientId])
|
||||
if (err) throw err
|
||||
}
|
||||
|
||||
|
||||
const syncList = async(socket: LX.Sync.Server.Socket) => {
|
||||
// socket.data.snapshotFilePath = getSnapshotFilePath(socket.keyInfo)
|
||||
if (socket.keyInfo.snapshotKey) {
|
||||
const listData = await getSnapshot(socket.keyInfo.snapshotKey)
|
||||
// console.log(socket.keyInfo)
|
||||
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) {
|
||||
console.log('handleMergeListDataFromSnapshot')
|
||||
await handleMergeListDataFromSnapshot(socket, listData)
|
||||
return
|
||||
}
|
||||
|
@ -379,7 +380,6 @@ const syncList = async(socket: LX.Sync.Server.Socket) => {
|
|||
await handleSyncList(socket)
|
||||
}
|
||||
|
||||
|
||||
// export default async(_wss: LX.Sync.Server.SocketServer, socket: LX.Sync.Server.Socket) => {
|
||||
// if (!wss) {
|
||||
// wss = _wss
|
||||
|
@ -402,7 +402,6 @@ const syncList = async(socket: LX.Sync.Server.Socket) => {
|
|||
|
||||
// syncingId = socket.keyInfo.clientId
|
||||
// await syncList(socket).then(async() => {
|
||||
// // if (newListData) registerUpdateSnapshotTask(socket, { ...newListData })
|
||||
// return finishedSync(socket)
|
||||
// }).finally(() => {
|
||||
// syncingId = null
|
|
@ -1,21 +1,26 @@
|
|||
import type http from 'http'
|
||||
import { SYNC_CODE } from '@common/constants'
|
||||
import {
|
||||
aesEncrypt,
|
||||
aesDecrypt,
|
||||
rsaEncrypt,
|
||||
getIP,
|
||||
} from '../utils/tools'
|
||||
import querystring from 'node:querystring'
|
||||
import { getIP } from './utils'
|
||||
import { createClientKeyInfo, getClientKeyInfo, saveClientKeyInfo } from '../data'
|
||||
import { aesDecrypt, aesEncrypt, getComputerName, rsaEncrypt } from '../utils'
|
||||
import { toMD5 } from '@common/utils/nodejs'
|
||||
import { getUserSpace, createClientKeyInfo } from '../user'
|
||||
import { toMD5 } from '../utils'
|
||||
import { getComputerName } from '../../utils'
|
||||
|
||||
const requestIps = new Map<string, number>()
|
||||
|
||||
|
||||
const getAvailableIP = (req: http.IncomingMessage) => {
|
||||
let ip = getIP(req)
|
||||
return ip && (requestIps.get(ip) ?? 0) < 10 ? ip : null
|
||||
}
|
||||
|
||||
const verifyByKey = (encryptMsg: string, userId: string) => {
|
||||
const keyInfo = getClientKeyInfo(userId)
|
||||
const userSpace = getUserSpace()
|
||||
const keyInfo = userSpace.dataManage.getClientKeyInfo(userId)
|
||||
if (!keyInfo) return null
|
||||
let text
|
||||
try {
|
||||
|
@ -28,7 +33,7 @@ const verifyByKey = (encryptMsg: string, userId: string) => {
|
|||
const deviceName = text.replace(SYNC_CODE.authMsg, '') || 'Unknown'
|
||||
if (deviceName != keyInfo.deviceName) {
|
||||
keyInfo.deviceName = deviceName
|
||||
saveClientKeyInfo(keyInfo)
|
||||
userSpace.dataManage.saveClientKeyInfo(keyInfo)
|
||||
}
|
||||
return aesEncrypt(SYNC_CODE.helloMsg, keyInfo.key)
|
||||
}
|
||||
|
@ -43,7 +48,7 @@ const verifyByCode = (encryptMsg: string, password: string) => {
|
|||
let text
|
||||
try {
|
||||
text = aesDecrypt(encryptMsg, key)
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
// console.log(text)
|
||||
|
@ -53,6 +58,8 @@ const verifyByCode = (encryptMsg: string, password: string) => {
|
|||
const deviceName = data[2] || 'Unknown'
|
||||
const isMobile = data[3] == 'lx_music_mobile'
|
||||
const keyInfo = createClientKeyInfo(deviceName, isMobile)
|
||||
const userSpace = getUserSpace()
|
||||
userSpace.dataManage.saveClientKeyInfo(keyInfo)
|
||||
return rsaEncrypt(Buffer.from(JSON.stringify({
|
||||
clientId: keyInfo.clientId,
|
||||
key: keyInfo.key,
|
||||
|
@ -66,7 +73,6 @@ export const authCode = async(req: http.IncomingMessage, res: http.ServerRespons
|
|||
let code = 401
|
||||
let msg: string = SYNC_CODE.msgAuthFailed
|
||||
|
||||
// console.log(req.headers)
|
||||
let ip = getAvailableIP(req)
|
||||
if (ip) {
|
||||
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
|
||||
msg = SYNC_CODE.msgBlockedIp
|
||||
}
|
||||
// console.log(req.headers)
|
||||
|
||||
res.writeHead(code)
|
||||
res.end(msg)
|
||||
}
|
||||
|
||||
const verifyConnection = (encryptMsg: string, userId: string) => {
|
||||
const keyInfo = getClientKeyInfo(userId)
|
||||
const userSpace = getUserSpace()
|
||||
const keyInfo = userSpace.dataManage.getClientKeyInfo(userId)
|
||||
if (!keyInfo) return false
|
||||
let text
|
||||
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 url from 'node:url'
|
||||
import { WebSocketServer } from 'ws'
|
||||
import { modules, callObj } from './modules'
|
||||
import { modules, callObj } from '../modules'
|
||||
import { authCode, authConnect } from './auth'
|
||||
import log from '../log'
|
||||
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@common/constants'
|
||||
import { decryptMsg, encryptMsg, generateCode as handleGenerateCode } from './utils'
|
||||
import { getAddress } from '../utils'
|
||||
import { sendServerStatus } from '@main/modules/winMain/index'
|
||||
import { getClientKeyInfo, getServerId, saveClientKeyInfo } from '../data'
|
||||
import { getAddress } from '../../utils'
|
||||
import { SYNC_CLOSE_CODE, SYNC_CODE } from '../../constants'
|
||||
import { getUserSpace, releaseUserSpace, getServerId, initServerInfo } from '../user'
|
||||
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 = {
|
||||
|
@ -19,6 +20,7 @@ let status: LX.Sync.ServerStatus = {
|
|||
code: '',
|
||||
devices: [],
|
||||
}
|
||||
|
||||
let stopingServer = false
|
||||
|
||||
const codeTools: {
|
||||
|
@ -51,6 +53,7 @@ const syncData = async(socket: LX.Sync.Server.Socket) => {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
const registerLocalSyncEvent = async(wss: LX.Sync.Server.SocketServer) => {
|
||||
for (const module of Object.values(modules)) {
|
||||
module.registerEvent(wss)
|
||||
|
@ -63,10 +66,11 @@ const unregisterLocalSyncEvent = () => {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
const checkDuplicateClient = (newSocket: LX.Sync.Server.Socket) => {
|
||||
for (const client of [...wss!.clients]) {
|
||||
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.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>
|
||||
|
||||
// // 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) {
|
||||
socket.close(SYNC_CLOSE_CODE.failed)
|
||||
return
|
||||
}
|
||||
keyInfo.lastSyncDate = Date.now()
|
||||
saveClientKeyInfo(keyInfo)
|
||||
keyInfo.lastConnectDate = Date.now()
|
||||
userSpace.dataManage.saveClientKeyInfo(keyInfo)
|
||||
// // socket.lx_keyInfo = keyInfo
|
||||
socket.keyInfo = keyInfo
|
||||
socket.userInfo = { name: 'default' }
|
||||
|
||||
checkDuplicateClient(socket)
|
||||
|
||||
try {
|
||||
|
@ -95,13 +102,12 @@ const handleConnection = async(socket: LX.Sync.Server.Socket, request: IncomingM
|
|||
return
|
||||
}
|
||||
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)
|
||||
sendServerStatus(status)
|
||||
socket.onClose(() => {
|
||||
status.devices.splice(status.devices.findIndex(k => k.clientId == keyInfo.clientId), 1)
|
||||
sendServerStatus(status)
|
||||
})
|
||||
|
||||
// console.log('connection', keyInfo.deviceName)
|
||||
log.info('connection', keyInfo.deviceName)
|
||||
|
@ -111,8 +117,8 @@ const handleConnection = async(socket: LX.Sync.Server.Socket, request: IncomingM
|
|||
}
|
||||
|
||||
const handleUnconnection = () => {
|
||||
console.log('unconnection')
|
||||
// console.log(socket.handshake.query)
|
||||
// console.log('unconnection')
|
||||
releaseUserSpace()
|
||||
}
|
||||
|
||||
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 = []
|
||||
msg2call.onDestroy()
|
||||
if (socket.isReady) {
|
||||
log.info('deconnection', socket.keyInfo.deviceName)
|
||||
log.info('deconnection', socket.userInfo.name, socket.keyInfo.deviceName)
|
||||
// events = {}
|
||||
if (!status.devices.length) handleUnconnection()
|
||||
} else {
|
||||
|
@ -247,14 +253,16 @@ const handleStartServer = async(port = 9527, ip = '0.0.0.0') => await new Promis
|
|||
if (!wss) return
|
||||
for (const client of wss.clients) handler(client)
|
||||
}
|
||||
|
||||
void handleConnection(socket, request)
|
||||
})
|
||||
|
||||
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.
|
||||
authConnection(request, err => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n')
|
||||
socket.destroy()
|
||||
return
|
||||
|
@ -270,6 +278,7 @@ const handleStartServer = async(port = 9527, ip = '0.0.0.0') => await new Promis
|
|||
const interval = setInterval(() => {
|
||||
wss?.clients.forEach(socket => {
|
||||
if (socket.isAlive == false) {
|
||||
log.info('alive check false:', socket.userInfo.name, socket.keyInfo.deviceName)
|
||||
socket.terminate()
|
||||
return
|
||||
}
|
||||
|
@ -321,7 +330,6 @@ const handleStopServer = async() => new Promise<void>((resolve, reject) => {
|
|||
})
|
||||
|
||||
export const stopServer = async() => {
|
||||
console.log('stop')
|
||||
codeTools.stop()
|
||||
if (!status.status) {
|
||||
status.status = false
|
||||
|
@ -345,16 +353,20 @@ export const stopServer = async() => {
|
|||
console.log(err)
|
||||
status.message = err.message
|
||||
}).finally(() => {
|
||||
stopingServer = false
|
||||
sendServerStatus(status)
|
||||
stopingServer = false
|
||||
})
|
||||
}
|
||||
|
||||
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 (status.status) await handleStopServer()
|
||||
|
||||
await migrateData(global.lxDataPath)
|
||||
await initServerInfo()
|
||||
|
||||
log.info('starting sync server')
|
||||
await handleStartServer(port).then(() => {
|
||||
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) {
|
||||
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
|
||||
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
|
||||
case 'list_remove':
|
||||
void global.lx.event_list.list_remove(data, true)
|
||||
await global.lx.event_list.list_remove(data, true)
|
||||
break
|
||||
case 'list_update':
|
||||
void global.lx.event_list.list_update(data, true)
|
||||
await global.lx.event_list.list_update(data, true)
|
||||
break
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
default:
|
||||
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 { 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 () => {
|
||||
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,
|
||||
})
|
||||
}
|
||||
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
|
||||
sendSyncAction({ action: 'select_mode', data: deviceName })
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ declare global {
|
|||
interface Socket extends WS.WebSocket {
|
||||
isAlive?: boolean
|
||||
isReady: boolean
|
||||
userInfo: { name: 'default' }
|
||||
keyInfo: ServerKeyInfo
|
||||
onClose: (handler: (err: Error) => (void | Promise<void>)) => () => void
|
||||
broadcast: (handler: (client: Socket) => void) => void
|
||||
|
|
|
@ -44,6 +44,8 @@ export default {
|
|||
host: appSetting['sync.client.host'],
|
||||
authCode: code,
|
||||
},
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
return {
|
||||
|
|
|
@ -46,6 +46,8 @@ export default () => {
|
|||
enable: appSetting['sync.enable'],
|
||||
port: appSetting['sync.server.port'],
|
||||
},
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
break
|
||||
|
@ -57,6 +59,8 @@ export default () => {
|
|||
enable: appSetting['sync.enable'],
|
||||
host: appSetting['sync.client.host'],
|
||||
},
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
break
|
||||
|
|
|
@ -56,6 +56,8 @@ export default () => {
|
|||
enable: appSetting['sync.enable'],
|
||||
port: appSetting['sync.server.port'],
|
||||
},
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
break
|
||||
|
@ -67,6 +69,8 @@ export default () => {
|
|||
enable: appSetting['sync.enable'],
|
||||
host: appSetting['sync.client.host'],
|
||||
},
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
break
|
||||
|
|
Loading…
Reference in New Issue