Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix(Issue #333) - Add functionality to clear Logs #334

Merged
merged 15 commits into from
Dec 14, 2019
22 changes: 22 additions & 0 deletions src/api/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,26 @@ logsRouter.get('/out/:domain', (req, res) => {
}
})

logsRouter.delete('/:domain', (req, res) => {
const { domain } = req.params

if (isProduction())
fs.writeFile(
`/home/myproxy/.pm2/logs/${domain}-out.log`,
'Log cleared\n',
err => {
if (err) console.log('Error deleting output log')
}
)
fs.writeFile(
`/home/myproxy/.pm2/logs/${domain}-err.log`,
'Log cleared\n',
err => {
if (err) console.log('Error deleting error log')
}
)

res.send('LOGS DELETED')
})

export default logsRouter
70 changes: 53 additions & 17 deletions src/public/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,24 @@ class MappingItem {
const mappingElement = document.createElement('li')
let iconClass
let iconColor
// LogClass is used to hide the button to download logs when
// pm2 is not managing the apps. Since pm2 is not managing the apps,
// the logs will not be located at the same location
// The variables below are to hide log related icons when pm2 is not
// being used to monitor the apps. These apps will not have status since
// they are not managed by pm2.
let settingClass
let logClass
if (data.status === 'online') {
iconClass = 'fa fa-circle mr-1 mt-1'
iconColor = 'rgba(50,255,50,0.5)'
logClass = 'fa fa-file-text-o ml-1 mt-1'
settingClass = 'ml-1 fa fa-cog'
} else if (data.status === 'not started') {
iconClass = ''
iconColor = 'transparent'
} else {
iconClass = 'fa fa-circle mr-1 mt-1'
iconColor = 'rgba(255, 50, 50, 0.5)'
logClass = 'fa fa-file-text-o ml-1 mt-1'
settingClass = 'ml-1 fa fa-cog'
}
mappingElement.classList.add(
'list-group-item',
Expand All @@ -64,38 +67,59 @@ class MappingItem {
)
domainList.appendChild(mappingElement)
mappingElement.innerHTML = `
<div style='width: 100%'>
<div style='display: flex'>
<div style="width: 100%">
<div style="display: flex">
<i class="${iconClass}" style="font-size: 15px; color: ${iconColor}">
</i>
<a class="font-weight-bold"
href="https://${data.fullDomain}">
<a class="font-weight-bold" href="https://${data.fullDomain}">
${data.fullDomain}
</a>
<small class="form-text text-muted ml-1">
PORT: ${data.port}
</small>
<a class="${logClass}"
<a
class="${logClass}"
style="font-size: 15px; color: rgba(255,50,50,0.5)"
href="/api/logs/err/${data.fullDomain}">
href="/api/logs/err/${data.fullDomain}"
>
</a>
<a class="${logClass}"
style="font-size: 15px; color: rgba(40,167,70,0.5)"
href="/api/logs/out/${data.fullDomain}">
<a
class="${logClass}"
style="font-size: 15px; color: rgba(40,167,70,0.5)"
href="/api/logs/out/${data.fullDomain}"
>
</a>
<div class="dropright">
<a href="#" role="button" data-toggle="dropdown" class="btn-link">
<span class="${settingClass}" style="font-size: 15px"> </span>
</a>
<div class="dropdown-menu">
<button
type="button"
class="btn btn-link deleteLogButton"
style="color: rgba(255,50,50,1)"
>
Clear Logs
</button>
</div>
</div>
</div>
<small class="form-text text-muted" style="display: inline-block;">
${data.gitLink}
</small>
</div>
<a href="/api/mappings/download/?fullDomain=${data.fullDomain}"
target="_blank" class="btn btn-sm btn-outline-success mr-3">
Download<i class="fa fa-download"></i>
<a
href="/api/mappings/download/?fullDomain=${data.fullDomain}"
target="_blank"
class="btn btn-sm btn-outline-success mr-3"
>
Download<i class="fa fa-download"></i>
</a>
<button
class="btn btn-sm btn-outline-danger mr-3 deleteButton"
type="button">
Delete
type="button"
>
Delete
</button>
`

Expand All @@ -113,6 +137,18 @@ class MappingItem {
})
}
}
const clearLogButton = helper.getElement('.deleteLogButton', mappingElement)
clearLogButton.onclick = (): void => {
if (
confirm(`Are you sure you want to clear ${data.fullDomain}'s logs?`)
) {
fetch(`/api/logs/${data.fullDomain}`, {
method: 'DELETE'
}).then(() => {
window.location.reload()
})
}
}
}
}

Expand Down
17 changes: 11 additions & 6 deletions src/tests/integration/log.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { startAppServer } from '../../server/server'
import uuidv4 from 'uuid/v4'
import { logAdapter } from '../helpers/logAdapter'

const TEST_PORT = process.env.PORT || 50608
Expand All @@ -16,16 +17,20 @@ describe('/api/logs', () => {
})

it('checks that output logs endpoint exists', async () => {
const subDomain = 'Cloud'
const domain = 'Walker'
const logResponse = await logAdapter(`/out/${subDomain}.${domain}`, 'GET')
const fullDomain = 'Cloud.Walker.com'
const logResponse = await logAdapter(`/out/${fullDomain}`, 'GET')
expect(logResponse.status).toEqual(200)
})

it('checks that error logs endpoint exists', async () => {
const subDomain = 'Luke'
const domain = 'Walker'
const logResponse = await logAdapter(`/err/${subDomain}.${domain}`, 'GET')
const fullDomain = 'Luke.Walker.com'
const logResponse = await logAdapter(`/err/${fullDomain}`, 'GET')
expect(logResponse.status).toEqual(200)
})

it('checks the delete endpoint exists', async () => {
const fullDomain = `${uuidv4()}.walker.com`
const logResponse = await logAdapter(`/${fullDomain}`, 'DELETE')
expect(logResponse.status).toEqual(200)
})
})