mirror of
https://gitlab.com/MrFry/mrfrys-node-server
synced 2026-04-28 03:07:38 +02:00
p2p submodule split, bug fixes
This commit is contained in:
@@ -0,0 +1,295 @@
|
||||
import path from 'node:path'
|
||||
import { paths } from '../../../utils/files'
|
||||
import utils from '../../../utils/utils'
|
||||
import { UserDirDataFile } from '../submodules/userFiles'
|
||||
import {
|
||||
SyncDataResult,
|
||||
SyncResponseBase,
|
||||
SyncResult,
|
||||
peerToString,
|
||||
updatePeersFile,
|
||||
} from './p2putils'
|
||||
import constants from '../../../constants'
|
||||
import { PeerInfo } from '../../../types/basicTypes'
|
||||
import { downloadFile } from '../../../utils/networkUtils'
|
||||
import logger from '../../../utils/logger'
|
||||
|
||||
interface UserFileToGet {
|
||||
fileName: string
|
||||
dir: string
|
||||
filePath: string
|
||||
data: UserDirDataFile
|
||||
peer: PeerInfo
|
||||
}
|
||||
|
||||
export interface NewUserFilesRequestBody {
|
||||
host: string
|
||||
newFiles: {
|
||||
[key: string]: {
|
||||
// key: dir
|
||||
[key: string]: UserDirDataFile // key: file name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Getting
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
export function getUserFiles(since: number): SyncResponseBase & {
|
||||
newFiles: {
|
||||
[key: string]: {
|
||||
[key: string]: UserDirDataFile
|
||||
}
|
||||
}
|
||||
} {
|
||||
const newFiles: SyncDataResult['userFiles']['newFiles'] = {}
|
||||
|
||||
const dirs = utils.ReadDir(paths.userFilesDir)
|
||||
dirs.forEach((dir) => {
|
||||
const userDirPath = path.join(paths.userFilesDir, dir)
|
||||
const dataFilePath = path.join(
|
||||
userDirPath,
|
||||
constants.userFilesDataFileName
|
||||
)
|
||||
|
||||
if (!utils.FileExists(dataFilePath)) {
|
||||
return
|
||||
}
|
||||
const dataFile =
|
||||
utils.ReadJSON<Map<string, UserDirDataFile>>(dataFilePath)
|
||||
Object.entries(dataFile).forEach(([fileName, data]) => {
|
||||
const mtime = utils.statFile(path.join(userDirPath, fileName)).mtime
|
||||
if (mtime.getTime() >= since) {
|
||||
if (!newFiles[dir]) {
|
||||
newFiles[dir] = {}
|
||||
}
|
||||
newFiles[dir][fileName] = data
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return { success: true, newFiles: newFiles }
|
||||
}
|
||||
|
||||
function setupFilesToGet(
|
||||
newFiles: SyncDataResult['userFiles']['newFiles'],
|
||||
peer: PeerInfo
|
||||
): UserFileToGet[] {
|
||||
const filesToGet: UserFileToGet[] = []
|
||||
Object.entries(newFiles).forEach(([dirName, userFilesDir]) => {
|
||||
Object.entries(userFilesDir).forEach(([fileName, data]) => {
|
||||
filesToGet.push({
|
||||
fileName: fileName,
|
||||
dir: dirName,
|
||||
filePath: path.join(paths.userFilesDir, dirName, fileName),
|
||||
data: data,
|
||||
peer: peer,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return filesToGet
|
||||
}
|
||||
|
||||
async function downloadUserFiles(filesToGet: UserFileToGet[]) {
|
||||
let addedFiles = 0
|
||||
for (const fileToGet of filesToGet) {
|
||||
const { peer, dir, fileName, filePath, data } = fileToGet
|
||||
|
||||
try {
|
||||
await downloadFile(
|
||||
{
|
||||
host: peer.host,
|
||||
port: peer.port,
|
||||
path: `/api/userFiles/${dir}/${fileName}`,
|
||||
},
|
||||
filePath,
|
||||
peer.http
|
||||
)
|
||||
|
||||
const dataFilePath = path.join(
|
||||
paths.userFilesDir,
|
||||
dir,
|
||||
constants.userFilesDataFileName
|
||||
)
|
||||
if (!utils.FileExists(dataFilePath)) {
|
||||
utils.WriteFile(JSON.stringify({}), dataFilePath)
|
||||
}
|
||||
const dataFile = utils.ReadJSON<{
|
||||
[key: string]: UserDirDataFile
|
||||
}>(dataFilePath)
|
||||
|
||||
if (dataFile[fileName]) {
|
||||
// dataFile[fileName].views += data.views // views are not unique
|
||||
dataFile[fileName].upvotes = dataFile[fileName].upvotes
|
||||
? dataFile[fileName].upvotes
|
||||
.concat(data.upvotes)
|
||||
.reduce((acc, x) => {
|
||||
if (acc.includes(x)) return acc
|
||||
return [...acc, x]
|
||||
}, [])
|
||||
: []
|
||||
dataFile[fileName].downvotes = dataFile[fileName].downvotes
|
||||
? dataFile[fileName].downvotes
|
||||
.concat(data.downvotes)
|
||||
.reduce((acc, x) => {
|
||||
if (acc.includes(x)) return acc
|
||||
return [...acc, x]
|
||||
}, [])
|
||||
: []
|
||||
} else {
|
||||
dataFile[fileName] = data
|
||||
}
|
||||
|
||||
utils.WriteFile(JSON.stringify(dataFile), dataFilePath)
|
||||
addedFiles += 1
|
||||
} catch (e) {
|
||||
logger.Log(`Unable to download "${fileName}": ${e.message}`)
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
return addedFiles
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Adding new
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
export async function handleNewUserFiles(
|
||||
props: NewUserFilesRequestBody & { peers: PeerInfo[] }
|
||||
): Promise<{
|
||||
success: boolean
|
||||
addedFileCount?: number
|
||||
message?: string
|
||||
}> {
|
||||
const result = await addNewUserFiles(props)
|
||||
|
||||
if (!result.success) {
|
||||
logger.Log(
|
||||
`Error while adding new user files: "${result.message}", from host: "${props.host}"`,
|
||||
'yellowbg'
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async function addNewUserFiles({
|
||||
newFiles,
|
||||
host,
|
||||
peers,
|
||||
}: NewUserFilesRequestBody & { peers: PeerInfo[] }): Promise<{
|
||||
success: boolean
|
||||
addedFileCount?: number
|
||||
message?: string
|
||||
}> {
|
||||
if (!newFiles || !host) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'newFiles or host key are missing from body',
|
||||
}
|
||||
}
|
||||
|
||||
const remotePeerInfo = peers.find((peer) => {
|
||||
return peerToString(peer) === host
|
||||
})
|
||||
|
||||
if (!remotePeerInfo) {
|
||||
return {
|
||||
success: false,
|
||||
message: "couldn't find remote peer info based on host",
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const filesToGet = setupFilesToGet(newFiles, remotePeerInfo)
|
||||
const addedFileCount = await downloadUserFiles(filesToGet)
|
||||
|
||||
logger.Log(
|
||||
`\tAdded ${logger.C(
|
||||
'blue'
|
||||
)}${addedFileCount}${logger.C()} new files from ${logger.C(
|
||||
'blue'
|
||||
)}${peerToString(remotePeerInfo)}${logger.C()}`
|
||||
)
|
||||
|
||||
return { success: true, addedFileCount: addedFileCount }
|
||||
} catch (e) {
|
||||
return { success: false, message: e.message }
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Syncing
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
export async function syncUserFiles(
|
||||
newData: (SyncDataResult['userFiles'] & { peer: PeerInfo })[],
|
||||
syncStart: number
|
||||
): Promise<SyncResult> {
|
||||
logger.Log('Syncing user files...')
|
||||
|
||||
const recievedUserFilesCount: (string | number)[][] = []
|
||||
let totalRecievedd = 0
|
||||
newData.forEach((res) => {
|
||||
const count = Object.values(res.newFiles).reduce((acc, data) => {
|
||||
totalRecievedd += Object.keys(data).length
|
||||
return acc + Object.keys(data).length
|
||||
}, 0)
|
||||
recievedUserFilesCount.push([peerToString(res.peer), count])
|
||||
})
|
||||
|
||||
if (totalRecievedd === 0) {
|
||||
logger.Log(
|
||||
`No peers returned any new files. User file sync successfully finished!`,
|
||||
'green'
|
||||
)
|
||||
return {
|
||||
old: {
|
||||
userFiles: 0,
|
||||
},
|
||||
added: {
|
||||
userFiles: 0,
|
||||
},
|
||||
final: {
|
||||
userFiles: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
logger.logTable([['', 'Files'], ...recievedUserFilesCount], {
|
||||
colWidth: [20],
|
||||
rowPrefix: '\t',
|
||||
})
|
||||
|
||||
const filesToGet: UserFileToGet[] = []
|
||||
newData.forEach((res) => {
|
||||
filesToGet.push(...setupFilesToGet(res.newFiles, res.peer))
|
||||
})
|
||||
|
||||
const addedFiles = await downloadUserFiles(filesToGet)
|
||||
|
||||
newData.forEach((res) => {
|
||||
updatePeersFile(res.peer, {
|
||||
lastUserFilesSync: syncStart,
|
||||
})
|
||||
})
|
||||
|
||||
logger.Log(
|
||||
`Successfully synced user files! Added ${addedFiles} files`,
|
||||
'green'
|
||||
)
|
||||
return {
|
||||
old: {
|
||||
userFiles: 0,
|
||||
},
|
||||
added: {
|
||||
userFiles: addedFiles,
|
||||
},
|
||||
final: {
|
||||
userFiles: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user