Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
0a06dc4
Add metrics server and AI query configuration
myakove Nov 23, 2025
844102d
build: Add metrics and ai optional dependencies to Docker image
myakove Nov 23, 2025
a026dd8
Add async database connection managers for PostgreSQL and Redis
myakove Nov 23, 2025
fd653a4
refactor: Remove defensive programming violations in database.py
myakove Nov 23, 2025
31c89f6
Add PostgreSQL and Redis services to docker-compose
myakove Nov 23, 2025
c96a849
feat: Add Alembic migration framework for database schema management
myakove Nov 23, 2025
d4a0470
Add SQLAlchemy models for GitHub webhook metrics database
myakove Nov 23, 2025
e5e122b
feat: Add metrics REST API and webhook event tracking
myakove Nov 23, 2025
8249c2d
test: Add comprehensive test coverage for metrics functionality
myakove Nov 23, 2025
15786a5
perf: Fix slow metrics server lifespan tests (60s → 1.7s)
myakove Nov 23, 2025
c51fe70
perf: Remove slow lifespan integration tests (71s → 27s)
myakove Nov 23, 2025
e16d8ba
fix: Run Alembic migrations on startup for metrics database
myakove Nov 23, 2025
f56db46
fix: Explicitly specify alembic.ini path for migrations
myakove Nov 23, 2025
e4a1199
feat: Add automatic database migration on container startup
myakove Nov 23, 2025
069ec12
ci: fix tox uv command
myakove Nov 23, 2025
5119e89
fix: Apply 15 CodeRabbit AI review improvements + test fixes
myakove Nov 23, 2025
f8ccb4d
chore: Update uv.lock for google-genai dependency migration
myakove Nov 23, 2025
5066139
fix: Allow NULL values for webhook action column (CRITICAL)
myakove Nov 23, 2025
58fdb7f
feat: Enable dual-location Alembic migrations for automated schema up…
myakove Nov 23, 2025
1b98390
refactor: revert to single-location Alembic strategy with auto-genera…
myakove Nov 23, 2025
8907d46
refactor: Remove unused Redis integration from metrics system
myakove Nov 23, 2025
a187bd5
chore: Remove final Redis configuration flag from alembic.ini
myakove Nov 23, 2025
5479477
chore: Remove Redis from config schema and dependencies
myakove Nov 23, 2025
e8b62f4
fix: Address CodeRabbit review comments - security and code quality
myakove Nov 23, 2025
0948416
chore: Update config examples and dependency lock file
myakove Nov 23, 2025
057eaed
fix: Fix 4 critical metrics tracking bugs
myakove Nov 23, 2025
e3c2833
fix: Restore processed_at to INSERT - schema requires it (NOT NULL wi…
myakove Nov 23, 2025
90e1588
fix: Eliminate time paradox by using database DEFAULT for processed_at
myakove Nov 23, 2025
024c43c
fix: Remove misleading warning emoji from Alembic INFO logs
myakove Nov 23, 2025
6458282
refactor: Address CodeRabbit review feedback for metrics system
myakove Nov 23, 2025
c7c88dc
fix: Add server_default to metrics_available column in Webhook model
myakove Nov 23, 2025
5396f86
fix: Address CodeRabbit review #3498000817 findings
myakove Nov 23, 2025
201d9ee
feat: add metrics dashboard implementation (with lint fixes)
myakove Nov 24, 2025
5dc43c0
feat: implement metrics trends API and connect frontend (re-commit af…
myakove Nov 24, 2025
1d04a7e
fix: handle custom time range visibility in dashboard
myakove Nov 24, 2025
c924aed
fix: align metrics dashboard visuals with log viewer and improve layout
myakove Nov 24, 2025
e300768
fix: improved time range handling in dashboard.js to match log viewer…
myakove Nov 24, 2025
fc9172f
fix: optimize chart readability by limiting x-axis ticks
myakove Nov 24, 2025
3b0f966
fix: initialize time range inputs on dashboard load
myakove Nov 24, 2025
df394db
feat: implement comprehensive metrics dashboard with real-time updates
myakove Nov 24, 2025
469a7a0
fix: prevent RuntimeError when WebSocket client disconnects
myakove Nov 24, 2025
19e1af7
fix: address CodeRabbit AI review findings from PR #943
myakove Nov 24, 2025
5dab8f8
feat: add PR commits count metrics to contributors endpoint
myakove Nov 24, 2025
17f7d1c
fix: resolve dashboard loading issues and simplify architecture
myakove Nov 24, 2025
7427532
fix: critical dashboard bugs - theme detection and loading spinner
myakove Nov 24, 2025
f4fe98a
fix: Event Distribution chart showing empty data
myakove Nov 24, 2025
403ae2c
fix: resolve dashboard loading issues and simplify architecture
myakove Nov 24, 2025
bfbe8e7
fix: dashboard theme detection and Event Trends debugging
myakove Nov 24, 2025
e5e4559
fix: Event Distribution chart text unreadable in dark theme
myakove Nov 24, 2025
029db2d
fix: Event Distribution legend text color (black instead of theme color)
myakove Nov 24, 2025
9e068fe
feat: implement repository filter functionality
myakove Nov 24, 2025
d871da7
feat: add trend calculations to metrics API
myakove Nov 24, 2025
d7b9fa9
fix: repository filter now filters all dashboard data
myakove Nov 24, 2025
2d5efdb
feat: add collapse functionality and chart settings modals
myakove Nov 24, 2025
7485cfd
fix: repository filter now updates KPIs and filters contributors
myakove Nov 24, 2025
38fd9c7
fix: remove extra closing div tag causing KPI boxes misalignment
myakove Nov 24, 2025
964b38e
fix: repository filter data mutation preventing clear
myakove Nov 24, 2025
f184dcb
feat: add pagination and user pull requests endpoint
myakove Nov 24, 2025
b9f85e2
feat: add user filter and pagination UI to metrics dashboard
myakove Nov 24, 2025
94d9edf
fix: standardize webhooks endpoint pagination format
myakove Nov 24, 2025
9a42798
fix: correct database method name in user-prs endpoint
myakove Nov 24, 2025
2c2e292
fix: update contributors queries for custom approval workflow
myakove Nov 24, 2025
51f7de4
fix: separate LGTM from approvals in contributors query
myakove Nov 24, 2025
70aa27a
feat: add LGTM tracking to contributors endpoint
myakove Nov 24, 2025
2daec2c
STDIN
myakove Nov 24, 2025
5942bc5
STDIN
myakove Nov 24, 2025
02a2343
fix: Use pr_number instead of number in User PRs table
myakove Nov 24, 2025
d6d3a51
fix: add error handling for User PRs endpoint
myakove Nov 24, 2025
7354244
feat: add pagination to all dashboard sections
myakove Nov 24, 2025
318f2c2
fix: extract data arrays from paginated contributors in populateUserF…
myakove Nov 24, 2025
f183085
fix: add kebab-case to camelCase conversion for pagination
myakove Nov 24, 2025
df36465
fix: add page/page_size parameters to fetchWebhooks API client
myakove Nov 24, 2025
463bc20
Merge branch 'main' of github.com:myk-org/github-webhook-server into …
myakove Nov 25, 2025
c715e70
fix: address CodeRabbit AI review findings from PR #943
myakove Nov 25, 2025
446976b
refactor: address CodeRabbit AI review findings from PR #943
myakove Nov 25, 2025
1d65e64
fix: comprehensive improvements from CodeRabbit AI review
myakove Nov 25, 2025
0985806
fix: address CodeRabbit AI review findings from PR #943
myakove Nov 25, 2025
4a2adb8
refactor: address CodeRabbit AI nitpick findings from PR #943
myakove Nov 25, 2025
b152936
Merge branch 'main' of github.com:myk-org/github-webhook-server into …
myakove Nov 25, 2025
0052537
fix: resolve PR Creators metrics data accuracy and security issues
myakove Nov 25, 2025
bf3773b
fix: correct repository percentage display in metrics dashboard
myakove Nov 25, 2025
41cb69f
fix: address CodeRabbit review comments for metrics and security
myakove Nov 25, 2025
1fb8993
fix: address CodeRabbit AI review comments for PR #943
myakove Nov 25, 2025
a985ec3
fix(dashboard): Update PR number field name from pr_number to number
myakove Nov 25, 2025
9a8aefb
fix: align metrics API field names with frontend expectations
myakove Nov 25, 2025
c1bb116
fix: address CodeRabbit review comments for PR #943
myakove Nov 26, 2025
9dda81f
fix: address critical security and performance issues from PR #943 re…
myakove Nov 26, 2025
e5c8144
fix: address CodeRabbit AI review comments for PR #943
myakove Nov 26, 2025
40c1407
fix: satisfy Ruff ARG002 linter for unused fixture parameter
myakove Nov 26, 2025
28d4f96
fix(migrations): revert to urllib.parse.quote for database URL encoding
myakove Nov 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ RUN mkdir -p $BIN_DIR \
&& mkdir -p $DATA_DIR \
&& mkdir -p $DATA_DIR/logs

COPY entrypoint.py pyproject.toml uv.lock README.md $APP_DIR/
COPY entrypoint.py pyproject.toml uv.lock README.md alembic.ini $APP_DIR/
COPY webhook_server $APP_DIR/webhook_server/
COPY scripts $APP_DIR/scripts/

Expand Down
94 changes: 94 additions & 0 deletions alembic.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Alembic configuration for GitHub Webhook Server metrics database
# See: https://alembic.sqlalchemy.org/en/latest/tutorial.html

[alembic]
# Path to migration scripts directory
script_location = webhook_server/migrations

# Template used to generate migration files
# %%(year)d%%(month).2d%%(day).2d_%%(hour).2d%%(minute).2d_%%(rev)s_%%(slug)s
# Example: 20250123_1430_abc123def456_add_metrics_table
file_template = %%(year)d%%(month).2d%%(day).2d_%%(hour).2d%%(minute).2d_%%(rev)s_%%(slug)s

# Prepends given value to alembic.script_location
# prepend_sys_path = .

# Timezone to use when rendering the date within the migration file
# as well as the filename.
# If specified, requires the python-dateutil library.
# timezone = UTC

# Max length of characters to apply to the "slug" field
# truncate_slug_length = 40

# Set to 'true' to run the environment during the 'revision' command
# revision_environment = false

# Set to 'true' to allow .pyc and .pyo files without a .py file to be detected
# sourceless = false

# Version table name - should match across all databases
version_table = alembic_version

# Version location specification
# Determines where Alembic stores version information
# IMPORTANT: This value is OVERRIDDEN dynamically in env.py
# env.py sets version_locations based on WEBHOOK_SERVER_DATA_DIR environment variable
# Default: {WEBHOOK_SERVER_DATA_DIR}/migrations/versions
# Container default: /home/podman/data/migrations/versions
# This placeholder is kept for reference only - actual path is set in env.py
version_locations = migrations/versions

# Version path separator (used if version_locations is specified)
# version_path_separator = os # Use os.pathsep. Default is ':'

# Database URL - loaded dynamically from config.yaml via env.py
# IMPORTANT: Do NOT set sqlalchemy.url here - it's loaded from config.yaml
# sqlalchemy.url =

# Logging configuration for Alembic migrations
# This section controls Alembic's own logging during migration operations
[loggers]
keys = root,sqlalchemy,alembic

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = INFO
handlers = console
qualname =

[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine

[logger_alembic]
level = INFO
handlers =
qualname = alembic

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

# Metrics-specific sections
# These are custom sections for webhook server metrics feature

[metrics]
# Feature flags for metrics migration
enable_postgres = true

# Migration behavior configuration
auto_migrate_on_startup = false
validate_schema_on_startup = true
57 changes: 57 additions & 0 deletions entrypoint.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import asyncio
import os
import subprocess
import sys
from pathlib import Path
Expand Down Expand Up @@ -41,10 +42,66 @@ def run_podman_cleanup() -> None:
print(f"ℹ️ Podman cleanup script not found at {cleanup_script}")


def run_database_migrations() -> None:
"""Run Alembic database migrations.

Only runs if ENABLE_METRICS_SERVER environment variable is set to "true".
Applies pending migrations with 'alembic upgrade head'.

Note: Migrations must be generated manually by developers:
alembic revision --autogenerate -m "Description"

Raises:
SystemExit: If migration fails (fail-fast behavior)
"""
metrics_enabled = os.environ.get("ENABLE_METRICS_SERVER") == "true"

if not metrics_enabled:
print("ℹ️ Metrics server disabled - skipping database migrations")
return

try:
alembic_ini = Path(__file__).parent / "alembic.ini"
versions_dir = Path(_config.data_dir) / "migrations" / "versions"

# Ensure versions directory exists (required for Alembic)
versions_dir.mkdir(parents=True, exist_ok=True)

print("⬆️ Applying database migrations...")
result = subprocess.run(
["uv", "run", "alembic", "-c", str(alembic_ini), "upgrade", "head"],
check=True,
capture_output=True,
text=True,
timeout=60,
cwd=Path(__file__).parent,
)
print(result.stdout)
if result.stderr:
print(result.stderr, file=sys.stderr)
print("✅ Database migrations completed successfully")
except subprocess.CalledProcessError as e:
print(f"❌ FATAL: Database migration failed: {e}", file=sys.stderr)
if e.stdout:
print(f"stdout: {e.stdout}", file=sys.stderr)
if e.stderr:
print(f"stderr: {e.stderr}", file=sys.stderr)
sys.exit(1)
except subprocess.TimeoutExpired:
print("❌ FATAL: Database migration timed out after 60 seconds", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"❌ FATAL: Unexpected error during database migration: {e}", file=sys.stderr)
sys.exit(1)


if __name__ == "__main__":
# Run Podman cleanup before starting the application
run_podman_cleanup()

# Run database migrations if metrics server is enabled
run_database_migrations()

result = asyncio.run(repository_and_webhook_settings(webhook_secret=_webhook_secret))

# Logging Configuration:
Expand Down
8 changes: 7 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module.exports = [
files: ["webhook_server/web/static/**/*.js"],
languageOptions: {
ecmaVersion: 2022,
sourceType: "script",
sourceType: "module",
globals: {
// Browser environment globals
window: "readonly",
Expand All @@ -23,6 +23,12 @@ module.exports = [
clearInterval: "readonly",
URLSearchParams: "readonly",
AbortController: "readonly",
URL: "readonly",
Blob: "readonly",
// CommonJS globals for conditional exports
module: "readonly",
// Chart.js global
Chart: "readonly",
},
},
rules: {
Expand Down
28 changes: 28 additions & 0 deletions examples/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,36 @@ log-level: INFO # Set global log level, change take effect immediately without s
log-file: webhook-server.log # Set global log file, change take effect immediately without server restart
mcp-log-file: mcp_server.log # Set global MCP log file, change take effect immediately without server restart
logs-server-log-file: logs_server.log # Set global Logs Server log file, change take effect immediately without server restart
metrics-server-log-file: metrics_server.log # Set global Metrics Server log file, change take effect immediately without server restart
mask-sensitive-data: true # Mask sensitive data in logs (default: true). Set to false for debugging (NOT recommended in production)

# Metrics Server Configuration (requires ENABLE_METRICS_SERVER=true environment variable)
# Provides PostgreSQL-based historical analytics and AI-powered natural language queries
# NOTE: For docker-compose deployments, use service name as hostname:
# - metrics-database host: github-webhook-server-postgres (defined in examples/docker-compose.yaml)
#
# ⚠️ SECURITY WARNING - TRUSTED NETWORK ONLY ⚠️
# The metrics dashboard and API endpoints (/api/v1/metrics/*) are UNAUTHENTICATED.
# These endpoints expose sensitive webhook data including user activity and repository statistics.
# REQUIREMENTS:
# - Deploy ONLY on trusted/private networks (VPN, internal network, localhost)
# - NEVER expose metrics endpoints directly to the public internet
# - Use a reverse proxy with authentication (OAuth, API keys) for external access
# - Enable SSL/TLS connections to PostgreSQL (sslmode=require) in production
metrics-database:
host: localhost # PostgreSQL server hostname (use 'github-webhook-server-postgres' in docker-compose)
port: 5432 # PostgreSQL server port
database: webhook_metrics # Database name for metrics
username: webhook_user # Database username
password: <DATABASE_PASSWORD> # Database password
pool-size: 20 # Connection pool size (default: 20)

# AI Query Configuration (optional - enables natural language queries in dashboard)
# Requires a valid Gemini API key - set ai-query-enabled to true only after configuring the key
# WARNING: Enabling ai-query-enabled without a valid gemini-api-key will cause AI query failures
gemini-api-key: <GEMINI_API_KEY> # Google Gemini API key for AI queries
ai-query-enabled: false # Enable AI-powered queries (default: false, requires valid API key)

# Server configuration
disable-ssl-warnings: true # Disable SSL warnings (useful in production to reduce log noise from SSL certificate issues)

Expand Down
38 changes: 38 additions & 0 deletions examples/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,34 @@
# ⚠️ SECURITY WARNING - TRUSTED NETWORK ONLY ⚠️
# The metrics dashboard and API endpoints (/api/v1/metrics/*) are UNAUTHENTICATED.
# These endpoints expose sensitive webhook data including user activity and repository statistics.
# REQUIREMENTS:
# - Deploy ONLY on trusted/private networks (VPN, internal network, localhost)
# - NEVER expose metrics endpoints directly to the public internet
# - Use a reverse proxy with authentication (OAuth, API keys) for external access
# - Consider using Docker network isolation to restrict access to metrics endpoints

services:
# PostgreSQL database for metrics storage
github-webhook-server-postgres:
image: postgres:16-alpine
container_name: github-webhook-server-postgres
environment:
- POSTGRES_DB=webhook_metrics
- POSTGRES_USER=webhook_user
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD} # Set POSTGRES_PASSWORD in .env or environment
volumes:
- postgres-data:/var/lib/postgresql/data
ports:
# Bind to localhost only - prevents external network access to database
# For production, consider removing ports entirely and using Docker network only
- "127.0.0.1:5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U webhook_user -d webhook_metrics"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped

github-webhook-server:
container_name: github-webhook-server
build: ghcr.io/myk-org/github-webhook-server:latest
Expand All @@ -18,7 +48,15 @@ services:
- VERIFY_CLOUDFLARE_IPS=1 # Verify hook request is from Cloudflare IPs
- ENABLE_LOG_SERVER=true # Enable log viewer endpoints (default: false)
- ENABLE_MCP_SERVER=false # Enable MCP server for AI agent integration (default: false)
- ENABLE_METRICS_SERVER=true # Enable metrics server with PostgreSQL (default: false)
ports:
- "5000:5000"
privileged: true
depends_on:
github-webhook-server-postgres:
condition: service_healthy
restart: unless-stopped

volumes:
postgres-data:
driver: local
9 changes: 6 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ output-format = "grouped"
select = ["E", "F", "W", "I", "B", "UP", "PLC0415", "ARG", "RUF059"]

[tool.ruff.lint.per-file-ignores]
"webhook_server/tests/*" = ["ARG"]
"webhook_server/tests/*" = ["ARG", "PLC0415"]

[tool.ruff.format]
exclude = [".git", ".venv", ".mypy_cache", ".tox", "__pycache__"]
Expand Down Expand Up @@ -75,6 +75,11 @@ dependencies = [
"pydantic>=2.8.0",
"psutil>=7.0.0",
"fastapi-mcp>=0.4.0",
"asyncpg>=0.29.0",
"alembic>=1.13.0",
"sqlalchemy[asyncio]>=2.0.0",
"google-genai>=0.1.0",
"aiosqlite>=0.21.0",
]

[[project.authors]]
Expand All @@ -90,8 +95,6 @@ homepage = "https://github.com/myakove/github-webhook-server"
repository = "https://github.com/myakove/github-webhook-server"
"Bug Tracker" = "https://github.com/myakove/github-webhook-server/issues"

[project.optional-dependencies]
tests = ["pytest-asyncio>=0.26.0", "pytest-xdist>=3.7.0"]

[build-system]
requires = ["hatchling"]
Expand Down
2 changes: 1 addition & 1 deletion tox.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ commands = [
[
"uv",
"run",
"--extra",
"--group",
"tests",
"pytest",
"-n",
Expand Down
Loading