Skip to content

Commit

Permalink
No commit message
Browse files Browse the repository at this point in the history
  • Loading branch information
breck7 committed Oct 28, 2024
1 parent d294e01 commit d20b20e
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 29 deletions.
24 changes: 18 additions & 6 deletions ScrollHub.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class ScrollHub {
this.sseClients = new Set()
this.globalLogFile = path.join(__dirname, "log.txt")
this.storyLogFile = path.join(__dirname, "writes.txt")
this.requestsLogPath = path.join(__dirname, "requests.csv")
this.dashboard = new Dashboard(this.globalLogFile)
}

Expand Down Expand Up @@ -209,17 +210,25 @@ class ScrollHub {
})
}

isSummarizing = false
async buildRequestsSummary() {
if (this.isSummarizing) return
this.isSummarizing = true
const dashboard = new Dashboard(this.globalLogFile)
await dashboard.processLogFile()
await fsp.writeFile(this.requestsLogPath, dashboard.csv, "utf8")
this.isSummarizing = false
}

initAnalytics() {
const checkWritePermissions = this.checkWritePermissions.bind(this)
if (!fs.existsSync(this.storyLogFile)) fs.writeFileSync(this.storyLogFile, "", "utf8")
const { app, folderCache } = this
app.use(this.logRequest.bind(this))

app.get("/dashboard.csv", async (req, res) => {
const { dashboard } = this
await dashboard.processLogFile()
const { csv } = dashboard
res.setHeader("Content-Type", "text/plain")
res.send(csv)
app.post("/summarizeRequests.htm", checkWritePermissions, async (req, res) => {
this.buildRequestsSummary()
res.send("Building Requests Summary")
})

app.get("/hostname.htm", (req, res) => res.send(req.hostname))
Expand Down Expand Up @@ -1179,6 +1188,9 @@ container 1000px
index.html ${this.hostname}
style font-size: 150%;
Traffic Data
requests.html
JSON | CSV | TSV
link folders.json JSON
link folders.csv CSV
Expand Down
73 changes: 51 additions & 22 deletions dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,15 @@ class Dashboard {
const regex = /^(read|write) ([^ ]+) ([^ ]+) (\d+) ([^ ]+) (.+)$/
const match = entry.match(regex)
if (match) {
const url = match[3]
// Extract filename from URL
const filename = url.split("/").pop().split("?")[0]

return {
method: match[1] === "read" ? "GET" : "POST",
folder: match[2],
url: match[3],
url: url,
filename: filename,
timestamp: new Date(parseInt(match[4])),
ip: match[5],
userAgent: match[6]
Expand All @@ -78,37 +83,61 @@ class Dashboard {
}

updateStats(entry) {
if (entry) {
const date = entry.timestamp.toISOString().split("T")[0]
const key = `${date}|${entry.folder}`

if (!this.stats[key]) {
this.stats[key] = {
date,
folder: entry.folder,
reads: 0,
writes: 0,
uniqueReaders: new Set(),
uniqueWriters: new Set()
}
if (!entry) return

const date = entry.timestamp.toISOString().split("T")[0]
const key = `${date}|${entry.folder}`

if (!this.stats[key])
this.stats[key] = {
date,
folder: entry.folder,
reads: 0,
writes: 0,
uniqueReaders: new Set(),
uniqueWriters: new Set(),
pageViews: new Map() // Track views per page
}

if (entry.method === "GET") {
this.stats[key].reads++
this.stats[key].uniqueReaders.add(entry.ip)
} else if (entry.method === "POST") {
this.stats[key].writes++
this.stats[key].uniqueWriters.add(entry.ip)
const row = this.stats[key]

if (entry.method === "GET") {
row.reads++
row.uniqueReaders.add(entry.ip)

// Update page views
if (entry.filename.endsWith(".html") || entry.filename.endsWith(".htm")) {
const currentViews = row.pageViews.get(entry.filename) || 0
row.pageViews.set(entry.filename, currentViews + 1)
}
} else if (entry.method === "POST") {
row.writes++
row.uniqueWriters.add(entry.ip)
}
}

getTopPages(pageViews, count = 5) {
return Array.from(pageViews.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, count)
.map(([filename]) => filename)
}

finalizeStats() {
Object.values(this.stats).forEach(stat => {
stat.readers = stat.uniqueReaders.size
stat.writers = stat.uniqueWriters.size

// Get top 5 pages and store them as rank1 through rank5
const topPages = this.getTopPages(stat.pageViews)
for (let i = 0; i < 5; i++) {
stat[`rank${i + 1}`] = topPages[i] || "" // Empty string if no page exists for this rank
}

// Clean up temporary data structures
delete stat.uniqueReaders
delete stat.uniqueWriters
delete stat.pageViews
})
}

Expand Down Expand Up @@ -145,9 +174,9 @@ class Dashboard {
}

get csv() {
const csvHeader = "Date,Folder,Reads,Readers,Writes,Writers\n"
const csvHeader = "Date,Folder,Reads,Readers,Writes,Writers,Rank1,Rank2,Rank3,Rank4,Rank5\n"
const csvRows = Object.values(this.stats)
.map(({ date, folder, reads, readers, writes, writers }) => `${date},${folder},${reads},${readers},${writes},${writers}`)
.map(({ date, folder, reads, readers, writes, writers, rank1, rank2, rank3, rank4, rank5 }) => `${date},${folder},${reads},${readers},${writes},${writers},${rank1},${rank2},${rank3},${rank4},${rank5}`)
.join("\n")
return csvHeader + csvRows
}
Expand Down
20 changes: 20 additions & 0 deletions requests.scroll
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
title Traffic Data

header.scroll
printTitle

container

Real time view
globe.html

button Refresh
link summarizeRequests.htm
post
// Anything

requests.csv
printTable

tableSearch
footer.scroll
2 changes: 1 addition & 1 deletion scrollHubEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ class EditorApp {
updateFooterLinks() {
const { folderName, folderNameText } = this
document.getElementById("gitClone").innerHTML =
`<a class="folderActionLink" href="/globe.html?folderName=${folderName}">traffic</a> · <a class="folderActionLink" href="/diff.htm/${folderName}">history</a> · <a class="folderActionLink" href="#" onclick="window.app.duplicate()">duplicate</a> · <a href="#" class="folderActionLink" onclick="window.app.renameFolder()">rename</a> · <a href="#" class="folderActionLink" onclick="window.app.deleteFolder()">delete</a>`
`<a class="folderActionLink" href="/globe.html?folderName=${folderName}">live traffic</a> · <a class="folderActionLink" href="/diff.htm/${folderName}">history</a> · <a class="folderActionLink" href="#" onclick="window.app.duplicate()">duplicate</a> · <a href="#" class="folderActionLink" onclick="window.app.renameFolder()">rename</a> · <a href="#" class="folderActionLink" onclick="window.app.deleteFolder()">delete</a>`
}

async renameFolder() {
Expand Down

0 comments on commit d20b20e

Please sign in to comment.