完成同步功能
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
const { aesEncrypt, aesDecrypt, createClientKeyInfo, getClientKeyInfo, setClientKeyInfo } = require('./utils')
|
||||
|
||||
const authMsg = 'lx-music auth::'
|
||||
const helloMsg = 'Hello~::^-^::'
|
||||
|
||||
exports.authCode = async(req, res, authCode) => {
|
||||
let code = 401
|
||||
let msg = 'Forbidden'
|
||||
// console.log(req.headers)
|
||||
if (req.headers.m) {
|
||||
label:
|
||||
if (req.headers.i) {
|
||||
const keyInfo = getClientKeyInfo(req.headers.i)
|
||||
if (!keyInfo) break label
|
||||
let text
|
||||
try {
|
||||
text = aesDecrypt(req.headers.m, keyInfo.key, keyInfo.iv)
|
||||
} catch (err) {
|
||||
break label
|
||||
}
|
||||
console.log(text)
|
||||
if (text.startsWith(authMsg)) {
|
||||
code = 200
|
||||
const deviceName = text.replace(authMsg, '') || 'Unknown'
|
||||
if (deviceName != keyInfo.deviceName) {
|
||||
keyInfo.deviceName = deviceName
|
||||
setClientKeyInfo(keyInfo)
|
||||
}
|
||||
msg = aesEncrypt(helloMsg, keyInfo.key, keyInfo.iv)
|
||||
}
|
||||
} else {
|
||||
let key = ''.padStart(16, Buffer.from(authCode).toString('hex'))
|
||||
const iv = Buffer.from(key.split('').reverse().join('')).toString('base64')
|
||||
key = Buffer.from(key).toString('base64')
|
||||
// console.log(authCode, key, iv)
|
||||
let text
|
||||
try {
|
||||
text = aesDecrypt(req.headers.m, key, iv)
|
||||
} catch (err) {
|
||||
break label
|
||||
}
|
||||
console.log(text)
|
||||
if (text.startsWith(authMsg)) {
|
||||
code = 200
|
||||
const deviceName = text.replace(authMsg, '') || 'Unknown'
|
||||
msg = aesEncrypt(JSON.stringify(createClientKeyInfo(deviceName)), key, iv)
|
||||
}
|
||||
}
|
||||
}
|
||||
res.writeHead(code)
|
||||
res.end(msg)
|
||||
}
|
||||
|
||||
exports.authConnect = async req => {
|
||||
const { i, t } = req._query
|
||||
label:
|
||||
if (i && t) {
|
||||
const keyInfo = getClientKeyInfo(i)
|
||||
if (!keyInfo) break label
|
||||
let text
|
||||
try {
|
||||
text = aesDecrypt(t, keyInfo.key, keyInfo.iv)
|
||||
} catch (err) {
|
||||
break label
|
||||
}
|
||||
if (text == 'lx-music connect') return
|
||||
}
|
||||
throw new Error('failed')
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
const { startServer, stopServer, getStatus } = require('./server')
|
||||
|
||||
module.exports = {
|
||||
startServer,
|
||||
stopServer,
|
||||
getStatus,
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
const http = require('http')
|
||||
const sio = require('socket.io')
|
||||
const { createHttpTerminator } = require('http-terminator')
|
||||
const modules = require('../modules')
|
||||
const { authCode, authConnect } = require('./auth')
|
||||
const { getAddress, getServerId, generateCode, getClientKeyInfo } = require('./utils')
|
||||
const syncList = require('./syncList')
|
||||
|
||||
|
||||
let status = {
|
||||
status: false,
|
||||
message: '',
|
||||
address: [],
|
||||
code: '',
|
||||
devices: [],
|
||||
}
|
||||
|
||||
const handleConnection = (io, socket) => {
|
||||
console.log('connection')
|
||||
// console.log(socket.handshake.query)
|
||||
for (const module of Object.values(modules)) {
|
||||
module.registerListHandler(io, socket)
|
||||
}
|
||||
}
|
||||
|
||||
const authConnection = (req, callback) => {
|
||||
// console.log(req.headers)
|
||||
// // console.log(req.auth)
|
||||
// console.log(req._query.authCode)
|
||||
authConnect(req).then(() => {
|
||||
callback(null, true)
|
||||
}).catch(err => {
|
||||
callback(err, false)
|
||||
})
|
||||
}
|
||||
|
||||
let httpTerminator = null
|
||||
let io = null
|
||||
|
||||
const handleStartServer = (port = 9527) => new Promise((resolve, reject) => {
|
||||
const httpServer = http.createServer((req, res) => {
|
||||
// console.log(req.url)
|
||||
let code
|
||||
let msg
|
||||
switch (req.url) {
|
||||
case '/hello':
|
||||
code = 200
|
||||
msg = 'Hello~::^-^::'
|
||||
break
|
||||
case '/id':
|
||||
code = 200
|
||||
msg = 'OjppZDo6' + getServerId()
|
||||
break
|
||||
case '/ah':
|
||||
authCode(req, res, status.code)
|
||||
break
|
||||
default:
|
||||
code = 401
|
||||
msg = 'Forbidden'
|
||||
break
|
||||
}
|
||||
if (!code) return
|
||||
res.writeHead(code)
|
||||
res.end(msg)
|
||||
})
|
||||
httpTerminator = createHttpTerminator({
|
||||
server: httpServer,
|
||||
})
|
||||
io = sio(httpServer, {
|
||||
path: '/sync',
|
||||
serveClient: false,
|
||||
connectTimeout: 10000,
|
||||
pingTimeout: 30000,
|
||||
maxHttpBufferSize: 3e6,
|
||||
allowRequest: authConnection,
|
||||
transports: ['websocket'],
|
||||
})
|
||||
|
||||
io.on('connection', async socket => {
|
||||
socket.on('disconnect', reason => {
|
||||
console.log('disconnect', reason)
|
||||
status.devices.splice(status.devices.findIndex(k => k.clientId == keyInfo.clientId), 1)
|
||||
global.lx_event.sync.status(status)
|
||||
})
|
||||
const keyInfo = getClientKeyInfo(socket.handshake.query.i)
|
||||
// socket.lx_keyInfo = keyInfo
|
||||
socket.data.keyInfo = keyInfo
|
||||
try {
|
||||
await syncList(io, socket)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
return
|
||||
}
|
||||
status.devices.push(keyInfo)
|
||||
handleConnection(io, socket, keyInfo)
|
||||
global.lx_event.sync.status(status)
|
||||
})
|
||||
|
||||
httpServer.on('error', error => {
|
||||
console.log(error)
|
||||
reject(error)
|
||||
})
|
||||
|
||||
httpServer.on('listening', () => {
|
||||
const addr = httpServer.address()
|
||||
const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port
|
||||
console.info(`Listening on ${bind}`)
|
||||
resolve()
|
||||
})
|
||||
|
||||
httpServer.listen(port)
|
||||
})
|
||||
|
||||
const handleStopServer = async() => {
|
||||
if (!httpTerminator) return
|
||||
await io.close()
|
||||
await httpTerminator.terminate().catch(() => {})
|
||||
io = null
|
||||
httpTerminator = null
|
||||
}
|
||||
|
||||
exports.stopServer = async() => {
|
||||
if (!status.status) return
|
||||
console.log('stoping sync server...')
|
||||
return handleStopServer().then(() => {
|
||||
console.log('sync server stoped')
|
||||
status.status = false
|
||||
status.message = ''
|
||||
status.address = []
|
||||
status.code = ''
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
status.message = err.message
|
||||
}).finally(() => {
|
||||
global.lx_event.sync.status(status)
|
||||
})
|
||||
}
|
||||
exports.startServer = async port => {
|
||||
if (status.status) await handleStopServer()
|
||||
|
||||
console.log('starting sync server...')
|
||||
return handleStartServer(port).then(() => {
|
||||
console.log('sync server started')
|
||||
status.status = true
|
||||
status.message = ''
|
||||
status.address = getAddress()
|
||||
status.code = generateCode()
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
status.status = false
|
||||
status.message = err.message
|
||||
status.address = []
|
||||
status.code = ''
|
||||
}).finally(() => {
|
||||
global.lx_event.sync.status(status)
|
||||
})
|
||||
}
|
||||
|
||||
exports.getStatus = () => status
|
||||
|
||||
exports.generateCode = async() => {
|
||||
status.code = generateCode()
|
||||
global.lx_event.sync.status(status)
|
||||
return status.code
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const fsPromises = fs.promises
|
||||
const { app } = require('electron')
|
||||
const { encryptMsg, decryptMsg } = require('./utils')
|
||||
const SYNC_EVENT_NAMES = require('../event/name')
|
||||
const { common: COMMON_EVENT_NAME } = require('@main/events/_name')
|
||||
const { throttle } = require('@common/utils')
|
||||
|
||||
let io
|
||||
// const checkFile = path => {
|
||||
// fsPromises.access(path, fs.constants.R_OK | fs.constants.W_OK)
|
||||
// .then(() => console.log('can access'))
|
||||
// .catch(() => console.error('cannot access'))
|
||||
// }
|
||||
|
||||
const getRemoteListData = socket => new Promise((resolve, reject) => {
|
||||
console.log('getRemoteListData')
|
||||
const handleError = reason => {
|
||||
reject(new Error(reason))
|
||||
}
|
||||
const handleSuccess = enData => {
|
||||
socket.removeListener('disconnect', handleError)
|
||||
socket.removeListener('list:sync', handleSuccess)
|
||||
console.log('getRemoteListData', 'handleSuccess')
|
||||
const data = JSON.parse(decryptMsg(socket.data.keyInfo, enData))
|
||||
if (!data) return reject(new Error('Get remote list data failed'))
|
||||
if (data.action != 'getData') return
|
||||
resolve(data.data)
|
||||
}
|
||||
|
||||
socket.on('disconnect', handleError)
|
||||
socket.on('list:sync', handleSuccess)
|
||||
socket.emit('list:sync', encryptMsg(socket.data.keyInfo, JSON.stringify({ action: 'getData', data: 'all' })))
|
||||
})
|
||||
|
||||
const getLocalListData = () => new Promise((resolve, reject) => {
|
||||
const handleSuccess = ({ action, data }) => {
|
||||
if (action !== 'getData') return
|
||||
global.lx_event.sync.off(SYNC_EVENT_NAMES.sync_handle_list, handleSuccess)
|
||||
resolve(data)
|
||||
}
|
||||
global.lx_event.sync.on(SYNC_EVENT_NAMES.sync_handle_list, handleSuccess)
|
||||
global.lx_event.sync.sync_list({
|
||||
action: 'getData',
|
||||
})
|
||||
})
|
||||
const getSyncMode = keyInfo => new Promise((resolve, reject) => {
|
||||
const handleSuccess = ({ action, data }) => {
|
||||
if (action !== 'selectMode') return
|
||||
global.lx_event.sync.off(SYNC_EVENT_NAMES.sync_handle_list, handleSuccess)
|
||||
resolve(data)
|
||||
}
|
||||
global.lx_event.sync.on(SYNC_EVENT_NAMES.sync_handle_list, handleSuccess)
|
||||
global.lx_event.sync.sync_list({
|
||||
action: 'selectMode',
|
||||
data: keyInfo,
|
||||
})
|
||||
})
|
||||
|
||||
const finishedSync = socket => {
|
||||
return socket.emit('list:sync', encryptMsg(socket.data.keyInfo, JSON.stringify({
|
||||
action: 'finished',
|
||||
})))
|
||||
}
|
||||
|
||||
const setLocalList = listData => {
|
||||
global.lx_event.sync.sync_list({
|
||||
action: 'setData',
|
||||
data: listData,
|
||||
})
|
||||
}
|
||||
const setRemotelList = async(socket, listData) => {
|
||||
if (!io) return
|
||||
const sockets = await io.fetchSockets()
|
||||
for (const socket of sockets) {
|
||||
// if (excludeIds.includes(socket.data.keyInfo.clientId)) continue
|
||||
socket.emit('list:sync', encryptMsg(socket.data.keyInfo, JSON.stringify({ action: 'setData', data: listData })))
|
||||
}
|
||||
}
|
||||
|
||||
let writeFilePromises = {}
|
||||
const updateSnapshot = (path, data) => {
|
||||
console.log('updateSnapshot', path)
|
||||
let writeFilePromise = writeFilePromises[path] || Promise.resolve()
|
||||
return writeFilePromise.then(() => {
|
||||
writeFilePromise = writeFilePromises[path] = fsPromises.writeFile(path, data)
|
||||
return writeFilePromise
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const createListDataObj = listData => {
|
||||
const listDataObj = {}
|
||||
for (const list of listData.userList) listDataObj[list.id] = list
|
||||
return listDataObj
|
||||
}
|
||||
|
||||
const handleMergeList = (sourceList, targetList, addMusicLocationType) => {
|
||||
let newList
|
||||
switch (addMusicLocationType) {
|
||||
case 'top':
|
||||
newList = [...targetList.list, ...sourceList.list]
|
||||
break
|
||||
case 'bottom':
|
||||
default:
|
||||
newList = [...sourceList.list, ...targetList.list]
|
||||
break
|
||||
}
|
||||
const map = {}
|
||||
const ids = []
|
||||
switch (addMusicLocationType) {
|
||||
case 'top':
|
||||
newList = [...targetList.list, ...sourceList.list]
|
||||
for (let i = newList.length - 1; i > -1; i--) {
|
||||
const item = newList[i]
|
||||
if (map[item.songmid]) continue
|
||||
ids.unshift(item.songmid)
|
||||
map[item.songmid] = item
|
||||
}
|
||||
break
|
||||
case 'bottom':
|
||||
default:
|
||||
newList = [...sourceList.list, ...targetList.list]
|
||||
for (const item of newList) {
|
||||
if (map[item.songmid]) continue
|
||||
ids.push(item.songmid)
|
||||
map[item.songmid] = item
|
||||
}
|
||||
break
|
||||
}
|
||||
return {
|
||||
...sourceList,
|
||||
list: ids.map(id => map[id]),
|
||||
}
|
||||
}
|
||||
const mergeList = (sourceListData, targetListData) => {
|
||||
const addMusicLocationType = global.appSetting.list.addMusicLocationType
|
||||
const newListData = {}
|
||||
newListData.defaultList = handleMergeList(sourceListData.defaultList, targetListData.defaultList, addMusicLocationType)
|
||||
newListData.loveList = handleMergeList(sourceListData.loveList, targetListData.loveList, addMusicLocationType)
|
||||
|
||||
const listDataObj = createListDataObj(sourceListData)
|
||||
newListData.userList = [...sourceListData.userList]
|
||||
|
||||
for (const list of targetListData.userList) {
|
||||
const targetList = listDataObj[list.id]
|
||||
if (targetList) {
|
||||
targetList.list = handleMergeList(targetList, list, addMusicLocationType).list
|
||||
} else {
|
||||
newListData.userList.push(list)
|
||||
}
|
||||
}
|
||||
|
||||
return newListData
|
||||
}
|
||||
const overwriteList = (sourceListData, targetListData) => {
|
||||
const newListData = {}
|
||||
newListData.defaultList = sourceListData.defaultList
|
||||
newListData.loveList = sourceListData.loveList
|
||||
|
||||
const listDataObj = createListDataObj(sourceListData)
|
||||
newListData.userList = [...sourceListData.userList]
|
||||
|
||||
for (const list of targetListData.userList) {
|
||||
const targetList = listDataObj[list.id]
|
||||
if (targetList) continue
|
||||
newListData.userList.push(list)
|
||||
}
|
||||
|
||||
return newListData
|
||||
}
|
||||
|
||||
const handleMergeListData = async socket => {
|
||||
let isSelectingMode = false
|
||||
const handleDisconnect = () => {
|
||||
if (!isSelectingMode) return
|
||||
global.lx_event.sync.sync_list({
|
||||
action: 'closeSelectMode',
|
||||
})
|
||||
}
|
||||
socket.on('disconnect', handleDisconnect)
|
||||
isSelectingMode = true
|
||||
const mode = await getSyncMode(socket.data.keyInfo)
|
||||
isSelectingMode = false
|
||||
const [remoteListData, localListData] = await Promise.all([getRemoteListData(socket), getLocalListData()])
|
||||
console.log('handleMergeListData', 'remoteListData, localListData')
|
||||
let listData
|
||||
switch (mode) {
|
||||
case 'merge_local_remote':
|
||||
listData = mergeList(localListData, remoteListData)
|
||||
break
|
||||
case 'merge_remote_local':
|
||||
listData = mergeList(remoteListData, localListData)
|
||||
break
|
||||
case 'overwrite_local_remote':
|
||||
listData = overwriteList(localListData, remoteListData)
|
||||
break
|
||||
case 'overwrite_remote_local':
|
||||
listData = overwriteList(remoteListData, localListData)
|
||||
break
|
||||
case 'overwrite_local_remote_full':
|
||||
listData = localListData
|
||||
break
|
||||
case 'overwrite_remote_local_full':
|
||||
listData = remoteListData
|
||||
break
|
||||
case 'none': return
|
||||
case 'cancel':
|
||||
socket.disconnect(true)
|
||||
throw new Error('cancel')
|
||||
}
|
||||
return listData
|
||||
}
|
||||
|
||||
const handleSyncList = async socket => {
|
||||
const [remoteListData, localListData] = await Promise.all([getRemoteListData(socket), getLocalListData()])
|
||||
console.log('handleSyncList', 'remoteListData, localListData')
|
||||
const listData = {}
|
||||
if (localListData.defaultList.list.length || localListData.loveList.list.length || localListData.userList.length) {
|
||||
if (remoteListData.defaultList.list.length || remoteListData.loveList.list.length || remoteListData.userList.length) {
|
||||
const mergedList = await handleMergeListData(socket)
|
||||
console.log('handleMergeListData', 'mergedList')
|
||||
console.log(mergedList)
|
||||
if (!mergedList) return
|
||||
listData.defaultList = mergedList.defaultList
|
||||
listData.loveList = mergedList.loveList
|
||||
listData.userList = mergedList.userList
|
||||
setLocalList(mergedList)
|
||||
setRemotelList(socket, mergedList)
|
||||
} else {
|
||||
setRemotelList(socket, localListData)
|
||||
listData.defaultList = localListData.defaultList
|
||||
listData.loveList = localListData.loveList
|
||||
listData.userList = localListData.userList
|
||||
}
|
||||
} else {
|
||||
if (remoteListData.defaultList.list.length || remoteListData.loveList.list.length || remoteListData.userList.length) {
|
||||
setLocalList(remoteListData)
|
||||
listData.defaultList = remoteListData.defaultList
|
||||
listData.loveList = remoteListData.loveList
|
||||
listData.userList = remoteListData.userList
|
||||
} else {
|
||||
listData.defaultList = localListData.defaultList
|
||||
listData.loveList = localListData.loveList
|
||||
listData.userList = localListData.userList
|
||||
}
|
||||
}
|
||||
return updateSnapshot(socket.data.snapshotFilePath, JSON.stringify({
|
||||
defaultList: listData.defaultList,
|
||||
loveList: listData.loveList,
|
||||
userList: listData.userList,
|
||||
})).then(() => {
|
||||
socket.data.isCreatedSnapshot = true
|
||||
return listData
|
||||
})
|
||||
}
|
||||
|
||||
const mergeListDataFromSnapshot = (sourceList, targetList, snapshotList, addMusicLocationType) => {
|
||||
const removedListIds = new Set()
|
||||
const sourceListItemIds = new Set()
|
||||
const targetListItemIds = new Set()
|
||||
for (const m of sourceList.list) sourceListItemIds.add(m.songmid)
|
||||
for (const m of targetList.list) targetListItemIds.add(m.songmid)
|
||||
for (const m of snapshotList.list) {
|
||||
if (!sourceListItemIds.has(m.songmid)) removedListIds.add(m.songmid)
|
||||
}
|
||||
for (const m of snapshotList.list) {
|
||||
if (!targetListItemIds.has(m.songmid)) removedListIds.add(m.songmid)
|
||||
}
|
||||
|
||||
let newList
|
||||
const map = {}
|
||||
const ids = []
|
||||
switch (addMusicLocationType) {
|
||||
case 'top':
|
||||
newList = [...targetList.list, ...sourceList.list]
|
||||
for (let i = newList.length - 1; i > -1; i--) {
|
||||
const item = newList[i]
|
||||
if (map[item.songmid] || removedListIds.has(item.songmid)) continue
|
||||
ids.unshift(item.songmid)
|
||||
map[item.songmid] = item
|
||||
}
|
||||
break
|
||||
case 'bottom':
|
||||
default:
|
||||
newList = [...sourceList.list, ...targetList.list]
|
||||
for (const item of newList) {
|
||||
if (map[item.songmid] || removedListIds.has(item.songmid)) continue
|
||||
ids.push(item.songmid)
|
||||
map[item.songmid] = item
|
||||
}
|
||||
break
|
||||
}
|
||||
return {
|
||||
...sourceList,
|
||||
list: ids.map(id => map[id]),
|
||||
}
|
||||
}
|
||||
const handleMergeListDataFromSnapshot = async(socket, snapshot) => {
|
||||
const addMusicLocationType = global.appSetting.list.addMusicLocationType
|
||||
const [remoteListData, localListData] = await Promise.all([getRemoteListData(socket), getLocalListData()])
|
||||
console.log('handleMergeListDataFromSnapshot', 'remoteListData, localListData')
|
||||
const newListData = {}
|
||||
newListData.defaultList = mergeListDataFromSnapshot(localListData.defaultList, remoteListData.defaultList, snapshot.defaultList, addMusicLocationType)
|
||||
newListData.loveList = mergeListDataFromSnapshot(localListData.loveList, remoteListData.loveList, snapshot.loveList, addMusicLocationType)
|
||||
const localUserListData = createListDataObj(localListData)
|
||||
const remoteUserListData = createListDataObj(remoteListData)
|
||||
const snapshotUserListData = createListDataObj(snapshot)
|
||||
const removedListIds = new Set()
|
||||
const localUserListIds = new Set()
|
||||
const remoteUserListIds = new Set()
|
||||
for (const l of localListData.userList) localUserListIds.add(l.id)
|
||||
for (const l of remoteListData.userList) remoteUserListIds.add(l.id)
|
||||
|
||||
for (const l of snapshot.userList) {
|
||||
if (!localUserListIds.has(l.id)) removedListIds.add(l.id)
|
||||
}
|
||||
for (const l of snapshot.userList) {
|
||||
if (!remoteUserListIds.has(l.id)) removedListIds.add(l.id)
|
||||
}
|
||||
|
||||
let newUserList = []
|
||||
for (const list of localListData.userList) {
|
||||
if (removedListIds.has(list.id)) continue
|
||||
const remoteList = remoteUserListData[list.id]
|
||||
let newList
|
||||
if (remoteList) {
|
||||
newList = mergeListDataFromSnapshot(list, remoteList, snapshotUserListData[list.id], addMusicLocationType)
|
||||
} else {
|
||||
newList = { ...list }
|
||||
}
|
||||
newUserList.push(newList)
|
||||
}
|
||||
for (const list of remoteListData.userList) {
|
||||
if (removedListIds.has(list.id) || localUserListData[list.id]) continue
|
||||
newUserList.push({ ...list })
|
||||
}
|
||||
newListData.userList = newUserList
|
||||
setLocalList(newListData)
|
||||
setRemotelList(socket, newListData)
|
||||
return updateSnapshot(socket.data.snapshotFilePath, JSON.stringify({
|
||||
defaultList: newListData.defaultList,
|
||||
loveList: newListData.loveList,
|
||||
userList: newListData.userList,
|
||||
})).then(() => {
|
||||
socket.data.isCreatedSnapshot = true
|
||||
return newListData
|
||||
})
|
||||
}
|
||||
|
||||
const registerUpdateSnapshotTask = (socket, snapshot) => {
|
||||
if (!socket.data.isCreatedSnapshot) return
|
||||
const handleUpdateSnapshot = throttle(({ defaultList, loveList, userList }) => {
|
||||
if (defaultList != null) snapshot.defaultList = defaultList
|
||||
if (loveList != null) snapshot.loveList = loveList
|
||||
if (userList != null) snapshot.userList = userList
|
||||
updateSnapshot(socket.data.snapshotFilePath, JSON.stringify(snapshot))
|
||||
}, 10000)
|
||||
global.lx_event.common.on(COMMON_EVENT_NAME.saveMyList, handleUpdateSnapshot)
|
||||
socket.on('disconnect', () => {
|
||||
global.lx_event.common.off(COMMON_EVENT_NAME.saveMyList, handleUpdateSnapshot)
|
||||
})
|
||||
}
|
||||
|
||||
const syncList = async socket => {
|
||||
socket.data.snapshotFilePath = path.join(app.getPath('userData'), `snapshot-${Buffer.from(socket.data.keyInfo.clientId).toString('hex').substring(0, 10)}.json`)
|
||||
let fileData
|
||||
let isSyncRequired = false
|
||||
try {
|
||||
fileData = await fsPromises.readFile(socket.data.snapshotFilePath)
|
||||
fileData = JSON.parse(fileData)
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') throw err
|
||||
isSyncRequired = true
|
||||
}
|
||||
console.log('isSyncRequired', isSyncRequired)
|
||||
if (isSyncRequired) return handleSyncList(socket)
|
||||
return handleMergeListDataFromSnapshot(socket, fileData)
|
||||
}
|
||||
|
||||
module.exports = (_io, socket) => {
|
||||
io = _io
|
||||
return syncList(socket).then(newListData => {
|
||||
registerUpdateSnapshotTask(socket, { ...newListData })
|
||||
return finishedSync(socket)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
const { networkInterfaces } = require('os')
|
||||
const { randomBytes, createCipheriv, createDecipheriv } = require('crypto')
|
||||
const getStore = require('@common/store')
|
||||
const STORE_NAME = 'sync'
|
||||
|
||||
exports.getAddress = () => {
|
||||
const nets = networkInterfaces()
|
||||
const results = []
|
||||
// console.log(nets)
|
||||
|
||||
for (const name of Object.keys(nets)) {
|
||||
for (const net of nets[name]) {
|
||||
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
|
||||
if (net.family === 'IPv4' && !net.internal) {
|
||||
results.push(net.address)
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
let serverId
|
||||
exports.getServerId = () => {
|
||||
if (serverId) return serverId
|
||||
const store = getStore(STORE_NAME)
|
||||
serverId = store.get('serverId')
|
||||
if (!serverId) {
|
||||
serverId = randomBytes(4 * 4).toString('base64')
|
||||
store.set('serverId', serverId)
|
||||
}
|
||||
return serverId
|
||||
}
|
||||
|
||||
let keyInfos
|
||||
exports.createClientKeyInfo = deviceName => {
|
||||
const keyInfo = {
|
||||
clientId: randomBytes(4 * 4).toString('base64'),
|
||||
key: randomBytes(16).toString('base64'),
|
||||
iv: randomBytes(16).toString('base64'),
|
||||
deviceName,
|
||||
}
|
||||
const store = getStore(STORE_NAME)
|
||||
if (!keyInfos) keyInfos = store.get('keys') || {}
|
||||
if (Object.keys(keyInfos).length > 101) throw new Error('max keys')
|
||||
|
||||
keyInfos[keyInfo.clientId] = keyInfo
|
||||
store.set('keys', keyInfos)
|
||||
return keyInfo
|
||||
}
|
||||
exports.setClientKeyInfo = keyInfo => {
|
||||
keyInfos[keyInfo.clientId] = keyInfo
|
||||
const store = getStore(STORE_NAME)
|
||||
store.set('keys', keyInfos)
|
||||
}
|
||||
exports.getClientKeyInfo = clientId => {
|
||||
if (!keyInfos) {
|
||||
const store = getStore(STORE_NAME)
|
||||
keyInfos = store.get('keys') || {}
|
||||
}
|
||||
return keyInfos[clientId] || null
|
||||
}
|
||||
|
||||
exports.generateCode = () => {
|
||||
return Math.random().toString().substring(2, 8)
|
||||
}
|
||||
|
||||
exports.aesEncrypt = (buffer, key, iv) => {
|
||||
const cipher = createCipheriv('aes-128-cbc', Buffer.from(key, 'base64'), Buffer.from(iv, 'base64'))
|
||||
return Buffer.concat([cipher.update(buffer), cipher.final()]).toString('base64')
|
||||
}
|
||||
|
||||
exports.aesDecrypt = (text, key, iv) => {
|
||||
const decipher = createDecipheriv('aes-128-cbc', Buffer.from(key, 'base64'), Buffer.from(iv, 'base64'))
|
||||
return Buffer.concat([decipher.update(Buffer.from(text, 'base64')), decipher.final()]).toString()
|
||||
}
|
||||
|
||||
exports.encryptMsg = (keyInfo, msg) => {
|
||||
return msg
|
||||
// if (!keyInfo) return ''
|
||||
// return exports.aesEncrypt(msg, keyInfo.key, keyInfo.iv)
|
||||
}
|
||||
|
||||
exports.decryptMsg = (keyInfo, enMsg) => {
|
||||
return enMsg
|
||||
// if (!keyInfo) return ''
|
||||
// let msg = ''
|
||||
// try {
|
||||
// msg = exports.aesDecrypt(enMsg, keyInfo.key, keyInfo.iv)
|
||||
// } catch (err) {
|
||||
// console.log(err)
|
||||
// }
|
||||
// return msg
|
||||
}
|
||||
Reference in New Issue
Block a user