Add richer metadata to logs and expandable detail panel #14
|
|
@ -60,7 +60,7 @@ class PRHandler {
|
|||
await this.handleStateChange(pullRequest, repository)
|
||||
}
|
||||
|
||||
log({ type: "pr", action, pr: payload.number, repo: repository.full_name, user: pullRequest.user.login })
|
||||
log({ type: "pr", action, pr: payload.number, repo: repository.full_name, user: pullRequest.user.login, title: pullRequest.title })
|
||||
}
|
||||
|
||||
static async handleOpened(pullRequest: Gitea.PullRequest, repository: Gitea.Repository) {
|
||||
|
|
@ -153,7 +153,7 @@ class CommentHandler {
|
|||
await this.handleDeleted(comment)
|
||||
}
|
||||
|
||||
log({ type: "comment", action, pr: issue.number, repo: repository.full_name, user: comment.user.login })
|
||||
log({ type: "comment", action, pr: issue.number, repo: repository.full_name, user: comment.user.login, body: comment.body.slice(0, 200) })
|
||||
}
|
||||
|
||||
static async handleCreated(issue: Gitea.Issue, comment: Gitea.Comment, repo: string) {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ import { basename } from "node:path"
|
|||
import { getConfig } from "./config"
|
||||
|
||||
export type LogEvent =
|
||||
| { type: "webhook"; eventType: string; repo: string }
|
||||
| { type: "webhook"; eventType: string; repo: string; sender?: string; ref?: string }
|
||||
| { type: "webhook-ignored"; eventType: string; repo: string }
|
||||
| { type: "pr"; action: string; pr: number; repo: string; user: string }
|
||||
| { type: "comment"; action: string; pr: number; repo: string; user: string }
|
||||
| { type: "pr"; action: string; pr: number; repo: string; user: string; title?: string }
|
||||
| { type: "comment"; action: string; pr: number; repo: string; user: string; body?: string }
|
||||
| { type: "comment-skipped"; pr: number; repo: string; commentId: number }
|
||||
| { type: "review"; action: string; pr: number; repo: string; user: string }
|
||||
| { type: "thread-auto-created"; pr: number; repo: string }
|
||||
|
|
@ -96,6 +96,9 @@ onLog((event) => {
|
|||
if ("repo" in event) parts.push(dim(event.repo))
|
||||
if ("pr" in event) parts.push(cyan(`#${event.pr}`))
|
||||
if ("user" in event) parts.push(dim(event.user))
|
||||
if ("sender" in event && event.sender) parts.push(dim(event.sender))
|
||||
if ("ref" in event && event.ref) parts.push(dim(event.ref))
|
||||
if ("title" in event && event.title) parts.push(event.title)
|
||||
if ("detail" in event) parts.push(event.detail)
|
||||
if ("command" in event) parts.push(event.command)
|
||||
if ("eventType" in event) parts.push(event.eventType)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ const server = serve({
|
|||
const payload = await req.json()
|
||||
const eventType = req.headers.get("X-Gitea-Event") || "unknown"
|
||||
const repo = (payload as any)?.repository?.full_name || "unknown"
|
||||
log({ type: "webhook", eventType, repo })
|
||||
const sender = (payload as any)?.sender?.login as string | undefined
|
||||
const ref = (payload as any)?.ref as string | undefined
|
||||
log({ type: "webhook", eventType, repo, sender, ref })
|
||||
|
||||
try {
|
||||
await handleGiteaWebhook(payload, eventType as any)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,19 @@ const typeColors: Record<string, string> = {
|
|||
error: "#f87171",
|
||||
}
|
||||
|
||||
function EventRow({ event }: { event: StoredLogEvent }) {
|
||||
const summaryFields = ["type", "ts"] as const
|
||||
|
||||
function getEventMeta(event: StoredLogEvent): Record<string, string> {
|
||||
const meta: Record<string, string> = {}
|
||||
for (const [key, value] of Object.entries(event)) {
|
||||
if (summaryFields.includes(key as any)) continue
|
||||
if (value === undefined) continue
|
||||
meta[key] = typeof value === "string" ? value : String(value)
|
||||
}
|
||||
return meta
|
||||
}
|
||||
|
||||
function EventRow({ event, index }: { event: StoredLogEvent; index: number }) {
|
||||
const color = typeColors[event.type] || "#9ca3af"
|
||||
const time = event.ts.slice(11, 19)
|
||||
|
||||
|
|
@ -35,14 +47,41 @@ function EventRow({ event }: { event: StoredLogEvent }) {
|
|||
details.push(msg)
|
||||
}
|
||||
|
||||
const meta = getEventMeta(event)
|
||||
const hasExtra = Object.keys(meta).length > 0
|
||||
|
||||
return (
|
||||
<tr style="border-bottom: 1px solid #2a2a2a">
|
||||
<td data-ts={event.ts} style="padding: 6px 12px; color: #6b7280; white-space: nowrap; font-size: 13px">{time}</td>
|
||||
<td style={`padding: 6px 12px; color: ${color}; white-space: nowrap; font-weight: 600; font-size: 13px`}>
|
||||
{event.type}
|
||||
</td>
|
||||
<td style="padding: 6px 12px; color: #d1d5db; font-size: 13px">{details.join(" ")}</td>
|
||||
</tr>
|
||||
<>
|
||||
<tr
|
||||
style={`border-bottom: 1px solid #2a2a2a${hasExtra ? "; cursor: pointer" : ""}`}
|
||||
data-row={index}
|
||||
onclick={hasExtra ? `(function(e){var d=document.getElementById('detail-${index}');if(d){d.style.display=d.style.display==='none'?'table-row':'none';e.currentTarget.querySelector('.expand-icon').textContent=d.style.display==='none'?'+':'-'}})(event)` : undefined}
|
||||
>
|
||||
<td style="padding: 6px 4px 6px 12px; color: #4a4a4a; font-size: 11px; width: 16px; user-select: none">
|
||||
{hasExtra && <span class="expand-icon">+</span>}
|
||||
</td>
|
||||
<td data-ts={event.ts} style="padding: 6px 12px; color: #6b7280; white-space: nowrap; font-size: 13px">{time}</td>
|
||||
<td style={`padding: 6px 12px; color: ${color}; white-space: nowrap; font-weight: 600; font-size: 13px`}>
|
||||
{event.type}
|
||||
</td>
|
||||
<td style="padding: 6px 12px; color: #d1d5db; font-size: 13px">{details.join(" ")}</td>
|
||||
</tr>
|
||||
{hasExtra && (
|
||||
<tr id={`detail-${index}`} style="display: none; background: #141414">
|
||||
<td></td>
|
||||
<td colspan="3" style="padding: 8px 12px 12px">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px 20px; font-size: 12px">
|
||||
{Object.entries(meta).map(([key, value]) => (
|
||||
<div style="display: flex; gap: 6px">
|
||||
<span style="color: #6b7280">{key}</span>
|
||||
<span style="color: #d1d5db; max-width: 500px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap">{value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -154,8 +193,8 @@ export function LogsPage(req: Request) {
|
|||
{/* Events table */}
|
||||
<table style="width: 100%; border-collapse: collapse">
|
||||
<tbody>
|
||||
{events.map((event) => (
|
||||
<EventRow event={event} />
|
||||
{events.map((event, i) => (
|
||||
<EventRow event={event} index={i} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user