diff options
| author | Elizabeth Hunt <me@liz.coffee> | 2025-12-14 20:36:24 -0800 |
|---|---|---|
| committer | Elizabeth Hunt <me@liz.coffee> | 2025-12-14 20:36:24 -0800 |
| commit | 6bf57766feb8321f860baf300140563cd9539053 (patch) | |
| tree | d80ff78c2a7f4dbea79f9ee850542aee1b735ef4 /src/types | |
| download | posthook-6bf57766feb8321f860baf300140563cd9539053.tar.gz posthook-6bf57766feb8321f860baf300140563cd9539053.zip | |
Init
Diffstat (limited to 'src/types')
| -rw-r--r-- | src/types/index.ts | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..fbfc70d --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,78 @@ +export enum ContentType { + JSON = 'json', + FORM = 'form', + MULTIPART = 'multipart', + TEXT = 'text', + RAW = 'raw', +} + +export interface NtfyConfig { + enabled: boolean; + server?: string; + topic?: string; +} + +export interface RouteConfig { + name: string; + contentType: ContentType; + hcaptchaProtected: boolean; + hcaptchaSecret?: string; + ntfy?: NtfyConfig; + requireToken?: boolean; +} + +export interface StoredRequest { + timestamp: number; + uuid: string; + routeName: string; + method: string; + headers: Record<string, string>; + body: unknown; + files?: Array<{ + filename: string; + contentType: string; + size: number; + path: string; + }>; +} + +const ROUTE_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i; + +export function isSafeRouteName(name: unknown): name is string { + if (typeof name !== 'string') return false; + if (name !== name.trim()) return false; + if (name === '.' || name === '..') return false; + if (name.includes('/') || name.includes('\\')) return false; + return ROUTE_NAME_PATTERN.test(name); +} + +export function isRouteConfig(obj: unknown): obj is RouteConfig { + if (typeof obj !== 'object' || obj === null) return false; + const r = obj as Record<string, unknown>; + + const validBasic = + isSafeRouteName(r.name) && + typeof r.contentType === 'string' && + Object.values(ContentType).includes(r.contentType as ContentType) && + typeof r.hcaptchaProtected === 'boolean' && + (r.hcaptchaProtected === false || typeof r.hcaptchaSecret === 'string'); + + if (!validBasic) return false; + + // Validate ntfy config if present + if (r.ntfy !== undefined) { + if (typeof r.ntfy !== 'object' || r.ntfy === null) return false; + const ntfy = r.ntfy as Record<string, unknown>; + if (typeof ntfy.enabled !== 'boolean') return false; + if (ntfy.enabled && (typeof ntfy.server !== 'string' || typeof ntfy.topic !== 'string')) { + return false; + } + } + + // Validate requireToken if present + if (r.requireToken !== undefined && typeof r.requireToken !== 'boolean') { + return false; + } + + return true; +} |
