diff --git a/lib/logs.js b/lib/logs.js index 18525d14..0983bf94 100644 --- a/lib/logs.js +++ b/lib/logs.js @@ -81,28 +81,31 @@ function getUnlimitedMachineLogs (deviceId, until = new Date().toISOString()) { })) } -function getMachineLogs (deviceId, until = new Date().toISOString()) { +function getMachineLogs (deviceId, until = new Date().toISOString(), limit = null, offset = 0) { const sql = `select id, log_level, timestamp, message from logs where device_id=$1 - and timestamp <= $3 + and timestamp <= $2 order by timestamp desc, serial desc - limit $2` + limit $3 + offset $4` - return Promise.all([db.any(sql, [ deviceId, NUM_RESULTS, until ]), getMachineName(deviceId)]) + return Promise.all([db.any(sql, [ deviceId, until, limit, offset ]), getMachineName(deviceId)]) .then(([logs, machineName]) => ({ logs: _.map(_.mapKeys(_.camelCase), logs), currentMachine: {deviceId, name: machineName} })) } -function simpleGetMachineLogs (deviceId, until = new Date().toISOString()) { +function simpleGetMachineLogs (deviceId, from = new Date(0).toISOString(), until = new Date().toISOString(), limit = null, offset = 0) { const sql = `select id, log_level, timestamp, message from logs where device_id=$1 + and timestamp >= $2 and timestamp <= $3 order by timestamp desc, serial desc - limit $2` + limit $4 + offset $5` - return db.any(sql, [ deviceId, NUM_RESULTS, until ]) + return db.any(sql, [ deviceId, from, until, limit, offset ]) .then(_.map(_.mapKeys(_.camelCase))) } diff --git a/lib/new-admin/graphql/schema.js b/lib/new-admin/graphql/schema.js index d1efb629..6bf2c18b 100644 --- a/lib/new-admin/graphql/schema.js +++ b/lib/new-admin/graphql/schema.js @@ -195,12 +195,12 @@ const typeDefs = gql` machines: [Machine] customers: [Customer] customer(customerId: ID!): Customer - machineLogs(deviceId: ID!): [MachineLog] + machineLogs(deviceId: ID!, from: Date, until: Date, limit: Int, offset: Int): [MachineLog] funding: [CoinFunds] serverVersion: String! uptime: [ProcessStatus] - serverLogs: [ServerLog] - transactions: [Transaction] + serverLogs(from: Date, until: Date, limit: Int, offset: Int): [ServerLog] + transactions(from: Date, until: Date, limit: Int, offset: Int): [Transaction] accounts: JSONObject config: JSONObject } @@ -250,11 +250,14 @@ const resolvers = { customers: () => customers.getCustomersList(), customer: (...[, { customerId }]) => customers.getCustomerById(customerId), funding: () => funding.getFunding(), - machineLogs: (...[, { deviceId }]) => logs.simpleGetMachineLogs(deviceId), + machineLogs: (...[, { deviceId, from, until, limit, offset }]) => + logs.simpleGetMachineLogs(deviceId, from, until, limit, offset), serverVersion: () => serverVersion, uptime: () => supervisor.getAllProcessInfo(), - serverLogs: () => serverLogs.getServerLogs(), - transactions: () => transactions.batch(), + serverLogs: (...[, { from, until, limit, offset }]) => + serverLogs.getServerLogs(from, until, limit, offset), + transactions: (...[, { from, until, limit, offset }]) => + transactions.batch(from, until, limit, offset), config: () => settingsLoader.getConfig(), accounts: () => settingsLoader.getAccounts() }, diff --git a/lib/new-admin/server-logs.js b/lib/new-admin/server-logs.js index e09b08bf..3e8c98b4 100644 --- a/lib/new-admin/server-logs.js +++ b/lib/new-admin/server-logs.js @@ -5,12 +5,14 @@ const db = require('../db') const NUM_RESULTS = 500 -function getServerLogs (until = new Date().toISOString()) { +function getServerLogs (from = new Date(0).toISOString(), until = new Date().toISOString(), limit = null, offset = 0) { const sql = `select id, log_level, timestamp, message from server_logs + where timestamp >= $1 and timestamp <= $2 order by timestamp desc - limit $1` + limit $3 + offset $4` - return db.any(sql, [ NUM_RESULTS ]) + return db.any(sql, [ from, until, limit, offset ]) .then(_.map(_.mapKeys(_.camelCase))) } diff --git a/lib/new-admin/transactions.js b/lib/new-admin/transactions.js index 249722aa..657ab4ea 100644 --- a/lib/new-admin/transactions.js +++ b/lib/new-admin/transactions.js @@ -23,9 +23,8 @@ function addNames (txs) { const camelize = _.mapKeys(_.camelCase) -function batch () { - const packager = _.flow(_.flatten, _.orderBy(_.property('created'), ['desc']), - _.take(NUM_RESULTS), _.map(camelize), addNames) +function batch (from = new Date(0).toISOString(), until = new Date().toISOString(), limit = null, offset = 0) { + const packager = _.flow(_.flatten, _.orderBy(_.property('created'), ['desc']), _.map(camelize), addNames) const cashInSql = `select 'cashIn' as tx_class, txs.*, c.phone as customer_phone, @@ -38,7 +37,8 @@ function batch () { ((not txs.send_confirmed) and (txs.created <= now() - interval $1)) as expired from cash_in_txs as txs left outer join customers c on txs.customer_id = c.id - order by created desc limit $2` + where txs.created >= $2 and txs.created <= $3 + order by created desc limit $4 offset $5` const cashOutSql = `select 'cashOut' as tx_class, txs.*, @@ -50,14 +50,18 @@ function batch () { c.name as customer_name, c.front_camera_path as customer_front_camera_path, c.id_card_photo_path as customer_id_card_photo_path, - (extract(epoch from (now() - greatest(txs.created, txs.confirmed_at))) * 1000) >= $2 as expired + (extract(epoch from (now() - greatest(txs.created, txs.confirmed_at))) * 1000) >= $1 as expired from cash_out_txs txs inner join cash_out_actions actions on txs.id = actions.tx_id and actions.action = 'provisionAddress' left outer join customers c on txs.customer_id = c.id - order by created desc limit $1` + where txs.created >= $2 and txs.created <= $3 + order by created desc limit $4 offset $5` - return Promise.all([db.any(cashInSql, [cashInTx.PENDING_INTERVAL, NUM_RESULTS]), db.any(cashOutSql, [NUM_RESULTS, REDEEMABLE_AGE])]) + return Promise.all([ + db.any(cashInSql, [cashInTx.PENDING_INTERVAL, from, until, limit, offset]), + db.any(cashOutSql, [REDEEMABLE_AGE, from, until, limit, offset]) + ]) .then(packager) } @@ -65,8 +69,7 @@ function getCustomerTransactions (customerId) { const packager = _.flow(it => { console.log() return it - }, _.flatten, _.orderBy(_.property('created'), ['desc']), - _.take(NUM_RESULTS), _.map(camelize), addNames) + }, _.flatten, _.orderBy(_.property('created'), ['desc']), _.map(camelize), addNames) const cashInSql = `select 'cashIn' as tx_class, txs.*, c.phone as customer_phone, diff --git a/new-lamassu-admin/src/components/LogsDownloaderPopper.js b/new-lamassu-admin/src/components/LogsDownloaderPopper.js index f5cfdfcc..37112c92 100644 --- a/new-lamassu-admin/src/components/LogsDownloaderPopper.js +++ b/new-lamassu-admin/src/components/LogsDownloaderPopper.js @@ -1,3 +1,4 @@ +import { useLazyQuery } from '@apollo/react-hooks' import { makeStyles } from '@material-ui/core' import classnames from 'classnames' import FileSaver from 'file-saver' @@ -128,10 +129,13 @@ const useStyles = makeStyles(styles) const ALL = 'all' const RANGE = 'range' -const LogsDownloaderPopover = ({ name, getTimestamp, logs, title }) => { +const LogsDownloaderPopover = ({ name, query, args, title, getLogs }) => { const [selectedRadio, setSelectedRadio] = useState(ALL) - const [range, setRange] = useState({ from: null, to: null }) + const [range, setRange] = useState({ from: null, until: null }) const [anchorEl, setAnchorEl] = useState(null) + const [fetchLogs] = useLazyQuery(query, { + onCompleted: data => createLogsFile(getLogs(data), range) + }) const classes = useStyles() @@ -143,49 +147,55 @@ const LogsDownloaderPopover = ({ name, getTimestamp, logs, title }) => { const handleRadioButtons = evt => { const selectedRadio = R.path(['target', 'value'])(evt) setSelectedRadio(selectedRadio) - if (selectedRadio === ALL) setRange({ from: null, to: null }) + if (selectedRadio === ALL) setRange({ from: null, until: null }) } const handleRangeChange = useCallback( - (from, to) => { - setRange({ from, to }) + (from, until) => { + setRange({ from, until }) }, [setRange] ) - const downloadLogs = (range, logs) => { - if (!range) return + const downloadLogs = (range, args, fetchLogs) => { + if (selectedRadio === ALL) { + fetchLogs({ + variables: { + ...args + } + }) + } - if (range.from && !range.to) range.to = moment() + if (!range || !range.from) return + if (range.from && !range.until) range.until = moment() + if (selectedRadio === RANGE) { + fetchLogs({ + variables: { + ...args, + from: range.from, + until: range.until + } + }) + } + } + + const createLogsFile = (logs, range) => { const formatDateFile = date => { return moment(date).format('YYYY-MM-DD_HH-mm') } - if (selectedRadio === ALL) { - const text = logs.map(it => JSON.stringify(it)).join('\n') - const blob = new window.Blob([text], { - type: 'text/plain;charset=utf-8' - }) - FileSaver.saveAs(blob, `${formatDateFile(new Date())}_${name}`) - return - } + const text = logs.map(it => JSON.stringify(it)).join('\n') + const blob = new window.Blob([text], { + type: 'text/plain;charset=utf-8' + }) - if (selectedRadio === RANGE) { - const text = logs - .filter(log => - moment(getTimestamp(log)).isBetween(range.from, range.to, 'day', '[]') - ) - .map(it => JSON.stringify(it)) - .join('\n') - const blob = new window.Blob([text], { - type: 'text/plain;charset=utf-8' - }) - FileSaver.saveAs( - blob, - `${formatDateFile(range.from)}_${formatDateFile(range.to)}_${name}` - ) - } + FileSaver.saveAs( + blob, + selectedRadio === ALL + ? `${formatDateFile(new Date())}_${name}` + : `${formatDateFile(range.from)}_${formatDateFile(range.until)}_${name}` + ) } const handleOpenRangePicker = event => { @@ -231,7 +241,7 @@ const LogsDownloaderPopover = ({ name, getTimestamp, logs, title }) => {