import { flatMap, map, catchError } from 'rxjs/operators' import { ok, fail } from 'typescript-monads' import { forkJoin, of } from 'rxjs' import { ping } from 'ping-rx' import { networkInterfaces, NetworkInterfaceInfo } from 'os' import { RxHR as http } from '@akanass/rx-http-request' export const createNumberedList = (num: number) => Array.from(Array(num).keys()) export const getIpRangeFromPrefix = (length = 256) => (prefix: string) => createNumberedList(length).map(a => `${prefix}.${a}`) export const getIpPrefixListFromNetworkInterfaces = (family: 'IPv4' | 'IPv6' = 'IPv4') => { const interfaceDict = networkInterfaces() return Object.keys(interfaceDict) .reduce((acc: readonly NetworkInterfaceInfo[], curr) => [...acc, ...interfaceDict[curr]], []) .filter(a => a.family === family && !a.internal) .map(c => c.address.split('.').slice(0, 3).join('.')) } // tslint:disable-next-line:readonly-array export const combined = (...args: string[][]) => args.reduce((acc, curr) => [...acc, ...curr], []) export const ipscan = (ipAddressPrefixes: readonly string[] = []) => (ipAddresses: readonly string[] = []) => { const standard = getIpPrefixListFromNetworkInterfaces().map(getIpRangeFromPrefix()) const pref = ipAddressPrefixes.map(getIpRangeFromPrefix()) // tslint:disable-next-line:readonly-array const toScan = combined(ipAddresses as string[], ...standard, ...pref) const pings = toScan.map(ip => ping(ip)()()) return forkJoin(pings).pipe( map(res => res.filter(a => a.isOk()).map(a => `http://${a.unwrap().host}/onvif/device_service`)), flatMap(urls => forkJoin( urls.map(url => http.post(url, { headers: { 'Content-Type': 'application/soap+xml; charset=utf-8;' }, body: '' }).pipe( map(res => res.response.statusCode === 200 ? ok(url) : fail('')), catchError(err => of(fail(err))) )) )), map(hosts => hosts.filter(b => b.isOk()).map(z => z.unwrap()))) }