Commit Graph

23 Commits

Author SHA1 Message Date
fuomag9
cf74451e9a feat: MUI date-time pickers, multiselect hosts with search, fix host list
- Replace native datetime-local inputs with @mui/x-date-pickers DateTimePicker
  (proper dark-themed calendar popover with time picker, DD/MM/YYYY HH:mm format,
  min/max constraints between pickers, 24h clock)
- Replace single-host Select with Autocomplete (multiple, disableCloseOnSelect):
  checkbox per option, chip display with limitTags=2, built-in search/filter
- getAnalyticsHosts() now unions traffic event hosts WITH all configured proxy host
  domains (parsed from proxyHosts.domains JSON), so every proxy appears in the list
- analytics-db: buildWhere accepts hosts: string[] (empty = all); uses inArray for
  multi-host filtering via drizzle-orm
- All 6 API routes updated: accept hosts param (comma-separated) instead of host

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 11:42:54 +01:00
fuomag9
9e2007eb0c feat: add custom date range picker, fix country click highlight on map
- Analytics default interval changed to 1h
- Add 'Custom' toggle option with datetime-local pickers (pre-filled to last 24h)
- Refactor analytics-db: buildWhere now takes from/to unix timestamps instead of Interval
- Export INTERVAL_SECONDS from analytics-db for route reuse
- All 6 API routes accept from/to params (fallback to interval if absent)
- Timeline bucket size computed from duration rather than hardcoded per interval
- Fix map country click highlight: bake isSelected into GeoJSON features (data-driven)
  instead of relying on Layer filter prop updates (unreliable in react-map-gl v8)
- Split highlight into countries-selected (data-driven) and countries-hover (filter-driven)
- Show tooltip at country centroid when selected via table, hover takes precedence

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 10:38:02 +01:00
fuomag9
8be69d2774 feat: add analytics dashboard with traffic monitoring
- Parse Caddy access logs every 30s into traffic_events SQLite table
- GeoIP country lookup via maxmind (GeoLite2-Country.mmdb)
- 90-day retention with automatic purge
- Analytics page with interval (24h/7d/30d) and per-host filtering:
  - Stats cards: total requests, unique IPs, blocked count, block rate
  - Requests-over-time area chart (ApexCharts)
  - SVG world choropleth map (d3-geo + topojson-client, React 19 compatible)
  - Top countries table with flag emojis
  - HTTP protocol donut chart
  - Top user agents horizontal bar chart
  - Recent blocked requests table with pagination
- Traffic (24h) summary card on Overview page linking to analytics
- 7 authenticated API routes under /api/analytics/
- Share caddy-logs volume with web container (read-only)
- group_add caddy GID to web container for log file read access

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 20:43:23 +01:00
fuomag9
75044c8d9b fix: harden security post-review (JWT exposure, rate limiter, token expiry, timing)
- Raw JWT never sent to browser: page.tsx uses peekLinkingToken (read-only),
  client sends opaque linkingId, API calls retrieveLinkingToken server-side
- link-account rate limiter now uses isRateLimited/registerFailedAttempt/
  resetAttempts correctly (count only failures, reset on success)
- linking_tokens gains expiresAt column (indexed) + opportunistic expiry
  purge on insert to prevent unbounded table growth
- secureTokenCompare fixed: pad+slice to expected length so timing is
  constant regardless of submitted token length (no length leak)
- autoLinkOAuth uses config.oauth.allowAutoLinking (boolean) instead of
  process.env truthy check that mishandles OAUTH_ALLOW_AUTO_LINKING=false
- Add Permissions-Policy header; restore X-Frame-Options for legacy UAs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 20:58:21 +01:00
fuomag9
66ad3e9431 fix: enforce unique provider+subject constraint and harden sync route
- Change providerSubjectIdx from index to uniqueIndex in schema.ts to
  prevent multiple users sharing the same (provider, subject) pair,
  which caused non-deterministic sign-in resolution via findFirst.
- Add migration 0008_unique_provider_subject.sql: DROP the existing
  non-unique index and CREATE UNIQUE INDEX in its place.
- Validate INSTANCE_SYNC_MAX_BYTES env var in sync route: fall back to
  10 MB default when the value is non-numeric (e.g. 'off') or
  non-positive, preventing NaN comparisons that silently disabled the
  size limit.
- Return a generic error message to callers on applySyncPayload /
  applyCaddyConfig failure instead of leaking the raw error string;
  the original message is still stored internally via setSlaveLastSync.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 18:41:12 +01:00
fuomag9
cb3c0a1536 fix: detect auth failure by response URL not status code to fix rate limiter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 18:38:25 +01:00
fuomag9
5d219095b3 fix: use rightmost XFF entry in rate limiter to prevent IP spoofing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 09:25:34 +01:00
fuomag9
f80b0c4735 feat: add geoip-status API route with auth
Returns whether GeoLite2-Country and GeoLite2-ASN databases are loaded,
used by the UI to show the GeoIP ready indicator in GeoBlockFields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 08:20:48 +01:00
fuomag9
25e1f2acee fix: make geoip-status a public endpoint (no auth required) 2026-02-25 01:31:06 +01:00
fuomag9
1231150550 fix: require auth on geoip-status endpoint 2026-02-25 01:24:11 +01:00
fuomag9
ccef82cca8 feat: add GeoIP status API route and improved geoblock UI 2026-02-25 01:22:04 +01:00
fuomag9
7e4df5e50b removed redirect feature 2026-02-13 22:53:11 +01:00
fuomag9
6d56cf2288 deprecate deadhosts, move it to the GUI as a custom response feature 2026-02-07 00:51:48 +01:00
fuomag9
90916bd709 various security fixes
Sync secrets encrypted at rest
Sync endpoint hardening
Avatar upload SVG restriction
2026-01-25 11:27:14 +01:00
fuomag9
6fb39dc809 Implement slave-master architecture 2026-01-25 01:39:36 +01:00
fuomag9
be21f46ad5 Added user tab and oauth2, streamlined readme 2025-12-28 15:14:56 +01:00
Claude
a2ae1f5baa Fix build errors and add Prisma stub generator for environments with network restrictions
This commit resolves multiple build errors and adds a workaround for environments
where Prisma engine binaries cannot be downloaded due to network restrictions.

Changes:
- Fix TypeScript error: Remove invalid request.ip property access in NextAuth route
- Add missing config import in auth.ts for sessionSecret
- Add dynamic = 'force-dynamic' to API routes to prevent static generation
- Create Prisma stub generator script for build-time type checking
- Update build script to use stub generator instead of prisma generate
- Add binaryTargets to Prisma schema configuration

The stub generator allows the Next.js build to complete successfully in environments
where Prisma binaries cannot be downloaded (403 Forbidden errors from binaries server).
The actual Prisma engines will need to be available at runtime in production deployments.

All routes are now properly configured as dynamic server-rendered routes.
2025-11-04 20:15:45 +00:00
Claude
44d8dabb78 Security improvements: Fix critical vulnerabilities
This commit addresses several critical security issues identified in the security audit:

1. Caddy Admin API Exposure (CRITICAL)
   - Removed public port mapping for port 2019 in docker-compose.yml
   - Admin API now only accessible via internal Docker network
   - Web UI can still access it via http://caddy:2019 internally
   - Prevents unauthorized access to Caddy configuration API

2. IP Spoofing in Rate Limiting (CRITICAL)
   - Updated getClientIp() to use Next.js request.ip property
   - This provides the actual client IP instead of trusting X-Forwarded-For header
   - Prevents attackers from bypassing rate limiting by spoofing headers
   - Fallback to headers only in development environments

3. Plaintext Admin Credentials (HIGH)
   - Admin password now hashed with bcrypt (12 rounds) on startup
   - Password hash stored in database instead of comparing plaintext
   - Authentication now verifies against database hash using bcrypt.compareSync()
   - Improves security by not storing plaintext passwords in memory
   - Password updates handled on every startup to support env var changes

Files modified:
- docker-compose.yml: Removed port 2019 public exposure
- app/api/auth/[...nextauth]/route.ts: Use actual client IP for rate limiting
- src/lib/auth.ts: Verify passwords against database hashes
- src/lib/init-db.ts: Hash and store admin password on startup

Security posture improved from C+ to B+
2025-11-04 18:25:48 +00:00
fuomag9
ad0225a16e Throttle login attempts and lock admin actions to privileged sessions 2025-11-04 00:00:22 +01:00
fuomag9
6ad7c49889 Add health check endpoint and fix Caddy HTTPS on startup 2025-11-03 19:17:13 +01:00
fuomag9
d9ced96e1b implement oauth2 login 2025-10-31 23:02:30 +01:00
fuomag9
29acf06f75 Swapped the entire UI to Material UI, applied a global dark theme, and removed all of the old styled-jsx/CSS-module styling 2025-10-31 21:03:02 +01:00
fuomag9
315192fb54 first rewrite commit 2025-10-31 20:08:28 +01:00