mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-09 15:11:29 -07:00
Initial commit
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import { HTTPException } from 'hono/http-exception'
|
||||
|
||||
import type { ContentfulStatusCode } from 'hono/utils/http-status'
|
||||
|
||||
/** Generates a new HTTPException with the given status and message as a JSON response.
|
||||
*
|
||||
* **Example:** `throw newHTTPException(401, 'unauthorized')`
|
||||
*/
|
||||
export function newHTTPException(status: ContentfulStatusCode, message: string): HTTPException {
|
||||
return new HTTPException(status, { message })
|
||||
}
|
||||
|
||||
export interface APIError {
|
||||
success: false
|
||||
error: {
|
||||
message: string
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { WorkersLogger } from 'workers-tagged-logger'
|
||||
|
||||
export type LogTagHints = {
|
||||
// add common tags here so that they show up as hints
|
||||
// in `logger.setTags()` and `logger.withTags()`
|
||||
url: string
|
||||
}
|
||||
|
||||
export const logger = new WorkersLogger<LogTagHints>()
|
||||
@@ -0,0 +1,72 @@
|
||||
import { redactUrl } from './url'
|
||||
|
||||
import type { Context } from 'hono'
|
||||
import type { HonoApp } from '../types'
|
||||
|
||||
export interface LogDataRequest {
|
||||
url: string
|
||||
method: string
|
||||
path: string
|
||||
/** Hono route for the request */
|
||||
routePath: string
|
||||
/* URL search params */
|
||||
searchParams: string
|
||||
headers: string
|
||||
/** Eyeball IP address of the request */
|
||||
ip?: string
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Get logdata from request
|
||||
*/
|
||||
export function getRequestLogData<T extends HonoApp>(
|
||||
c: Context<T>,
|
||||
requestStartTimestamp: number
|
||||
): LogDataRequest {
|
||||
const redactedUrl = redactUrl(c.req.url)
|
||||
return {
|
||||
url: redactedUrl.toString(),
|
||||
method: c.req.method,
|
||||
path: c.req.path,
|
||||
routePath: c.req.routePath,
|
||||
searchParams: redactedUrl.searchParams.toString(),
|
||||
headers: stringifyHeaders(c.req.raw.headers),
|
||||
ip:
|
||||
c.req.header('cf-connecting-ip') ||
|
||||
c.req.header('x-real-ip') ||
|
||||
c.req.header('x-forwarded-for'),
|
||||
timestamp: new Date(requestStartTimestamp).toISOString(),
|
||||
}
|
||||
}
|
||||
|
||||
const SENSITIVE_HEADER_NAMES = new Set([
|
||||
'authorization',
|
||||
'proxy-authorization',
|
||||
'cookie',
|
||||
'set-cookie',
|
||||
'cf-access-jwt-assertion',
|
||||
])
|
||||
|
||||
function isSensitiveHeader(name: string): boolean {
|
||||
const normalizedName = name.toLowerCase()
|
||||
|
||||
return (
|
||||
SENSITIVE_HEADER_NAMES.has(normalizedName) ||
|
||||
normalizedName.includes('token') ||
|
||||
normalizedName.includes('secret') ||
|
||||
normalizedName.includes('jwt') ||
|
||||
normalizedName.includes('signature') ||
|
||||
normalizedName.includes('session') ||
|
||||
normalizedName.endsWith('-key') ||
|
||||
normalizedName.endsWith('_key')
|
||||
)
|
||||
}
|
||||
|
||||
function stringifyHeaders(headers: Headers): string {
|
||||
return JSON.stringify(
|
||||
Object.fromEntries(
|
||||
Array.from(headers, ([name, value]) => [name, isSensitiveHeader(name) ? 'REDACTED' : value])
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/** Redacts keys from a url */
|
||||
export function redactUrl(_url: URL | string): URL {
|
||||
let url: URL
|
||||
if (typeof _url === 'string') {
|
||||
url = new URL(_url)
|
||||
} else {
|
||||
url = new URL(_url.toString()) // clone
|
||||
}
|
||||
for (const [key] of url.searchParams) {
|
||||
if (/key|token|secret|password|passwd|auth|credential/i.test(key)) {
|
||||
url.searchParams.set(key, 'REDACTED')
|
||||
}
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a URLSearchParams object into an array of "key=value" strings.
|
||||
*
|
||||
* @param searchParams - The URLSearchParams object to convert.
|
||||
* @returns An array of strings, where each string is in the format "key=value".
|
||||
*/
|
||||
export function searchParamsToArray(searchParams: URLSearchParams): string[] {
|
||||
const result: string[] = []
|
||||
for (const [key, value] of searchParams.entries()) {
|
||||
result.push(`${key}=${value}`)
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user