Skip to content

Commit

Permalink
Goaccess log reporting (#167)
Browse files Browse the repository at this point in the history
* First steps getting GoAccess working

* End to end flow starting to work

* Some refinements on logs tab

* Better looking log report page

* Goaccess settings

* Localization for goaccess settings

* Fetch report through API and show in modal

* Updated to work with live report files and format the names
  • Loading branch information
dshook authored Dec 22, 2024
1 parent 34db046 commit cc43146
Show file tree
Hide file tree
Showing 10 changed files with 776 additions and 78 deletions.
35 changes: 35 additions & 0 deletions src/api/ApiManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IAppDef } from '../containers/apps/AppDefinition'
import { ICaptainDefinition } from '../models/ICaptainDefinition'
import IGoAccessInfo from '../models/IGoAccessInfo'
import {
IProConfig,
IProFeatures,
Expand Down Expand Up @@ -527,6 +528,40 @@ export default class ApiManager {
)
}

getGoAccessInfo(): Promise<IGoAccessInfo> {
const http = this.http

return Promise.resolve() //
.then(http.fetch(http.GET, '/user/system/goaccess', {}))
}

updateGoAccessInfo(goAccessInfo: any) {
const http = this.http

return Promise.resolve() //
.then(
http.fetch(http.POST, '/user/system/goaccess', { goAccessInfo })
)
}
getGoAccessReports(appName: string) {
const http = this.http

return Promise.resolve() //
.then(
http.fetch(
http.GET,
`/user/system/goaccess/${appName}/files`,
{}
)
)
}
getGoAccessReport(reportUrl: string) {
const http = this.http

return Promise.resolve() //
.then(http.fetch(http.GET, reportUrl, {}))
}

changePass(oldPassword: string, newPassword: string) {
const http = this.http

Expand Down
8 changes: 7 additions & 1 deletion src/api/HttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export default class HttpClient {
})
.then(function (data) {
if (
typeof data.status == 'number' &&
data.status !== ErrorFactory.OKAY &&
data.status !== ErrorFactory.OK_PARTIALLY &&
data.status !== ErrorFactory.OKAY_BUILD_STARTED
Expand All @@ -89,7 +90,12 @@ export default class HttpClient {
// network request returns back.
return new Promise(function (resolve, reject) {
// data.data here is the "data" field inside the API response! {status: 100, description: "Login succeeded", data: {…}}
if (!self.isDestroyed) return resolve(data.data)
if (!self.isDestroyed)
return resolve(
typeof data.data !== 'undefined'
? data.data
: data
)
Logger.dev('Destroyed then not called')
})
})
Expand Down
189 changes: 189 additions & 0 deletions src/containers/apps/appDetails/AccessLogReports.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import { Button, Modal } from 'antd'
import { Component } from 'react'
import { localize } from '../../../utils/Language'
import { AppDetailsTabProps } from './AppDetails'

interface GoAccessReport {
name: string
lastModifiedTime: string
domainName: string
url: string
}
interface LogReportState {
reportList: GoAccessReport[]
reportOpen: string | undefined
reportHtml: string | undefined
}
export default class AccessLogReports extends Component<
AppDetailsTabProps,
LogReportState
> {
constructor(props: any) {
super(props)
this.state = {
reportList: [],
reportOpen: undefined,
reportHtml: undefined,
}
}
render() {
const groupedReports = this.state.reportList.reduce(
(acc: { [key: string]: GoAccessReport[] }, report) => {
const date = new Date(report.lastModifiedTime)
const year = date.getFullYear()
const month = date.toLocaleDateString(undefined, {
month: 'long',
})
const key = `${year} - ${month}`

if (!acc[key]) {
acc[key] = []
}

acc[key].push(report)

return acc
},
{}
)
const groupList = Object.entries(groupedReports)
.map(([key, reports]) => ({
key,
reports,
time: new Date(reports[0].lastModifiedTime).getTime(),
}))
.sort((a, b) => b.time - a.time)

return (
<>
<div
style={{
display: 'grid',
gridTemplateColumns: '1fr 3fr',
gap: '20px',
}}
>
{groupList.map(({ key, reports }) => (
<div style={{ display: 'contents' }} key={key}>
<h3>{key}</h3>
<div
style={{
display: 'flex',
flexWrap: 'wrap',
flexDirection: 'row',
gap: '40px',
}}
>
{Object.entries(
reports.reduce(
(
acc: {
[key: string]: GoAccessReport[]
},
report
) => {
if (!acc[report.domainName]) {
acc[report.domainName] = []
}
acc[report.domainName].push(report)
return acc
},
{}
)
).map(([domainName, reports]) => (
<div key={`${key}-${domainName}`}>
<h4>{domainName}</h4>
{reports.map((r) => (
<p key={r.name}>
<Button
onClick={() =>
this.onReportClick(
r.name,
r.url
)
}
>
{this.reportName(r)}
</Button>
</p>
))}
</div>
))}
</div>
<hr
style={{
gridColumn: '1 / 3',
height: '1px',
width: '100%',
opacity: 0.2,
}}
/>
</div>
))}
</div>

<Modal
closable={true}
footer={<div />}
title={this.state.reportOpen}
open={this.state.reportOpen !== undefined}
onCancel={() => this.onModalClose()}
loading={!this.state.reportHtml}
width="90vw"
style={{ top: 20 }}
>
{this.state.reportHtml && (
<iframe
title="GoAccess"
style={{
width: '100%',
minHeight: 'calc(85vh - 20px)',
}}
srcDoc={this.state.reportHtml}
></iframe>
)}
</Modal>
</>
)
}

componentDidMount() {
this.reFetchData()
}

reFetchData() {
const self = this
this.props.setLoading(true)

this.props.apiManager
.getGoAccessReports(this.props.apiData!.appDefinition.appName!)
.then((response) => {
self.setState({ reportList: response })
this.props.setLoading(false)
})
}

reportName(report: GoAccessReport) {
if (report.name.includes('Live')) {
return localize('goaccess_settings.live', 'Live')
}
const date = new Date(report.lastModifiedTime)

return date.toLocaleString(undefined, {
dateStyle: 'short',
timeStyle: 'medium',
})
}

onReportClick(reportName: string, reportUrl: string) {
this.setState({ reportOpen: reportName })

this.props.apiManager.getGoAccessReport(reportUrl).then((report) => {
this.setState({ reportHtml: report })
})
}

onModalClose() {
this.setState({ reportOpen: undefined, reportHtml: undefined })
}
}
Loading

0 comments on commit cc43146

Please sign in to comment.