feat: bring repo up to REPO_POLICIES standards #6

Merged
sneak merged 2 commits from feature/repo-policies-compliance into main 2026-03-01 19:01:45 +01:00
23 changed files with 94 additions and 61 deletions
Showing only changes of commit 69bbc958a7 - Show all commits

View File

@ -64,6 +64,6 @@ USER webhooker
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/.well-known/healthcheck.json || exit 1
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/.well-known/healthcheck || exit 1
CMD ["./webhooker"]

View File

@ -66,6 +66,39 @@ application lifecycle. It uses `log/slog` for structured logging, GORM
for database access, and SQLite (via `modernc.org/sqlite`, pure Go, no
CGO) for storage. HTTP routing uses chi.
### Rate Limiting
Global rate limiting middleware (e.g. per-IP throttling applied at the
router level) must **not** apply to webhook receiver endpoints
(`/webhook/{uuid}`). Webhook endpoints receive automated traffic from
external services at unpredictable rates, and blanket rate limits would
cause legitimate webhook deliveries to be dropped.
Instead, each webhook endpoint has its own individually configurable rate
limit, applied within the webhook handler itself. By default, no rate
limit is applied — webhook endpoints accept traffic as fast as it arrives.
Rate limits can be configured on a per-webhook basis in the application
when needed (e.g. to protect against a misbehaving sender).
### Database Architecture
webhooker uses separate SQLite database files rather than a single
monolithic database:
- **Main application database** — Stores application configuration and
all standard webapp data: users, sessions, API keys, and global
settings.
- **Per-processor databases** — Each processor (working name — a better
term is needed) gets its own dedicated SQLite database file containing:
input logs, processor logs, and all output queues for that specific
processor.
This separation provides several benefits: processor databases can be
independently backed up, rotated, or archived; a high-volume processor
won't cause lock contention or bloat affecting the main application; and
individual processor data can be cleanly deleted when a processor is
removed.
### Package Layout
All application code lives under `internal/` to prevent external imports.
@ -98,7 +131,7 @@ The main entry point is `cmd/webhooker/main.go`.
### API Endpoints
- `GET /` — Web UI index page
- `GET /.well-known/healthcheck.json` — Health check with uptime, version
- `GET /.well-known/healthcheck` — Health check with uptime, version
- `GET /s/*` — Static file serving (CSS, JS)
- `GET /metrics` — Prometheus metrics (requires basic auth)
- `POST /webhook/{uuid}` — Webhook receiver endpoint

View File

@ -3,16 +3,16 @@ package main
import (
"runtime"
"git.eeqj.de/sneak/webhooker/internal/config"
"git.eeqj.de/sneak/webhooker/internal/database"
"git.eeqj.de/sneak/webhooker/internal/globals"
"git.eeqj.de/sneak/webhooker/internal/handlers"
"git.eeqj.de/sneak/webhooker/internal/healthcheck"
"git.eeqj.de/sneak/webhooker/internal/logger"
"git.eeqj.de/sneak/webhooker/internal/middleware"
"git.eeqj.de/sneak/webhooker/internal/server"
"git.eeqj.de/sneak/webhooker/internal/session"
"go.uber.org/fx"
"sneak.berlin/go/webhooker/internal/config"
"sneak.berlin/go/webhooker/internal/database"
"sneak.berlin/go/webhooker/internal/globals"
"sneak.berlin/go/webhooker/internal/handlers"
"sneak.berlin/go/webhooker/internal/healthcheck"
"sneak.berlin/go/webhooker/internal/logger"
"sneak.berlin/go/webhooker/internal/middleware"
"sneak.berlin/go/webhooker/internal/server"
"sneak.berlin/go/webhooker/internal/session"
)
// Build-time variables set via -ldflags.

6
go.mod
View File

@ -1,11 +1,10 @@
module git.eeqj.de/sneak/webhooker
module sneak.berlin/go/webhooker
go 1.23.0
toolchain go1.24.1
require (
git.eeqj.de/sneak/webhooker/pkg/config v0.0.0-00010101000000-000000000000
github.com/99designs/basicauth-go v0.0.0-20230316000542-bf6f9cbbf0f8
github.com/getsentry/sentry-go v0.25.0
github.com/go-chi/chi v1.5.5
@ -22,6 +21,7 @@ require (
gorm.io/driver/sqlite v1.5.4
gorm.io/gorm v1.25.5
modernc.org/sqlite v1.28.0
sneak.berlin/go/webhooker/pkg/config v0.0.0-00010101000000-000000000000
)
require (
@ -85,4 +85,4 @@ require (
modernc.org/token v1.0.1 // indirect
)
replace git.eeqj.de/sneak/webhooker/pkg/config => ./pkg/config
replace sneak.berlin/go/webhooker/pkg/config => ./pkg/config

View File

@ -5,10 +5,10 @@ import (
"log/slog"
"os"
"git.eeqj.de/sneak/webhooker/internal/globals"
"git.eeqj.de/sneak/webhooker/internal/logger"
pkgconfig "git.eeqj.de/sneak/webhooker/pkg/config"
"go.uber.org/fx"
"sneak.berlin/go/webhooker/internal/globals"
"sneak.berlin/go/webhooker/internal/logger"
pkgconfig "sneak.berlin/go/webhooker/pkg/config"
// spooky action at a distance!
// this populates the environment

View File

@ -4,14 +4,14 @@ import (
"os"
"testing"
"git.eeqj.de/sneak/webhooker/internal/globals"
"git.eeqj.de/sneak/webhooker/internal/logger"
pkgconfig "git.eeqj.de/sneak/webhooker/pkg/config"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/fx"
"go.uber.org/fx/fxtest"
"sneak.berlin/go/webhooker/internal/globals"
"sneak.berlin/go/webhooker/internal/logger"
pkgconfig "sneak.berlin/go/webhooker/pkg/config"
)
// createTestConfig creates a test configuration file in memory

View File

@ -5,12 +5,12 @@ import (
"database/sql"
"log/slog"
"git.eeqj.de/sneak/webhooker/internal/config"
"git.eeqj.de/sneak/webhooker/internal/logger"
"go.uber.org/fx"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
_ "modernc.org/sqlite" // Pure Go SQLite driver
"sneak.berlin/go/webhooker/internal/config"
"sneak.berlin/go/webhooker/internal/logger"
)
// nolint:revive // DatabaseParams is a standard fx naming convention

View File

@ -4,12 +4,12 @@ import (
"context"
"testing"
"git.eeqj.de/sneak/webhooker/internal/config"
"git.eeqj.de/sneak/webhooker/internal/globals"
"git.eeqj.de/sneak/webhooker/internal/logger"
pkgconfig "git.eeqj.de/sneak/webhooker/pkg/config"
"github.com/spf13/afero"
"go.uber.org/fx/fxtest"
"sneak.berlin/go/webhooker/internal/config"
"sneak.berlin/go/webhooker/internal/globals"
"sneak.berlin/go/webhooker/internal/logger"
pkgconfig "sneak.berlin/go/webhooker/pkg/config"
)
func TestDatabaseConnection(t *testing.T) {

View File

@ -3,7 +3,7 @@ package handlers
import (
"net/http"
"git.eeqj.de/sneak/webhooker/internal/database"
"sneak.berlin/go/webhooker/internal/database"
)
// HandleLoginPage returns a handler for the login page (GET)

View File

@ -7,12 +7,12 @@ import (
"log/slog"
"net/http"
"git.eeqj.de/sneak/webhooker/internal/database"
"git.eeqj.de/sneak/webhooker/internal/globals"
"git.eeqj.de/sneak/webhooker/internal/healthcheck"
"git.eeqj.de/sneak/webhooker/internal/logger"
"git.eeqj.de/sneak/webhooker/internal/session"
"go.uber.org/fx"
"sneak.berlin/go/webhooker/internal/database"
"sneak.berlin/go/webhooker/internal/globals"
"sneak.berlin/go/webhooker/internal/healthcheck"
"sneak.berlin/go/webhooker/internal/logger"
"sneak.berlin/go/webhooker/internal/session"
)
// nolint:revive // HandlersParams is a standard fx naming convention

View File

@ -6,16 +6,16 @@ import (
"testing"
"time"
"git.eeqj.de/sneak/webhooker/internal/config"
"git.eeqj.de/sneak/webhooker/internal/database"
"git.eeqj.de/sneak/webhooker/internal/globals"
"git.eeqj.de/sneak/webhooker/internal/healthcheck"
"git.eeqj.de/sneak/webhooker/internal/logger"
"git.eeqj.de/sneak/webhooker/internal/session"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/fx"
"go.uber.org/fx/fxtest"
"sneak.berlin/go/webhooker/internal/config"
"sneak.berlin/go/webhooker/internal/database"
"sneak.berlin/go/webhooker/internal/globals"
"sneak.berlin/go/webhooker/internal/healthcheck"
"sneak.berlin/go/webhooker/internal/logger"
"sneak.berlin/go/webhooker/internal/session"
)
func TestHandleIndex(t *testing.T) {

View File

@ -5,7 +5,7 @@ import (
"net/http"
"time"
"git.eeqj.de/sneak/webhooker/internal/database"
"sneak.berlin/go/webhooker/internal/database"
)
type IndexResponse struct {

View File

@ -5,11 +5,11 @@ import (
"log/slog"
"time"
"git.eeqj.de/sneak/webhooker/internal/config"
"git.eeqj.de/sneak/webhooker/internal/database"
"git.eeqj.de/sneak/webhooker/internal/globals"
"git.eeqj.de/sneak/webhooker/internal/logger"
"go.uber.org/fx"
"sneak.berlin/go/webhooker/internal/config"
"sneak.berlin/go/webhooker/internal/database"
"sneak.berlin/go/webhooker/internal/globals"
"sneak.berlin/go/webhooker/internal/logger"
)
// nolint:revive // HealthcheckParams is a standard fx naming convention

View File

@ -6,8 +6,8 @@ import (
"os"
"time"
"git.eeqj.de/sneak/webhooker/internal/globals"
"go.uber.org/fx"
"sneak.berlin/go/webhooker/internal/globals"
)
// nolint:revive // LoggerParams is a standard fx naming convention

View File

@ -3,8 +3,8 @@ package logger
import (
"testing"
"git.eeqj.de/sneak/webhooker/internal/globals"
"go.uber.org/fx/fxtest"
"sneak.berlin/go/webhooker/internal/globals"
)
func TestNew(t *testing.T) {

View File

@ -6,9 +6,6 @@ import (
"net/http"
"time"
"git.eeqj.de/sneak/webhooker/internal/config"
"git.eeqj.de/sneak/webhooker/internal/globals"
"git.eeqj.de/sneak/webhooker/internal/logger"
basicauth "github.com/99designs/basicauth-go"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/cors"
@ -16,6 +13,9 @@ import (
ghmm "github.com/slok/go-http-metrics/middleware"
"github.com/slok/go-http-metrics/middleware/std"
"go.uber.org/fx"
"sneak.berlin/go/webhooker/internal/config"
"sneak.berlin/go/webhooker/internal/globals"
"sneak.berlin/go/webhooker/internal/logger"
)
// nolint:revive // MiddlewareParams is a standard fx naming convention

View File

@ -4,11 +4,11 @@ import (
"net/http"
"time"
"git.eeqj.de/sneak/webhooker/static"
sentryhttp "github.com/getsentry/sentry-go/http"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/prometheus/client_golang/prometheus/promhttp"
"sneak.berlin/go/webhooker/static"
)
func (s *Server) SetupRoutes() {
@ -63,7 +63,7 @@ func (s *Server) SetupRoutes() {
})
s.router.Get(
"/.well-known/healthcheck.json",
"/.well-known/healthcheck",
s.h.HandleHealthCheck(),
)

View File

@ -10,12 +10,12 @@ import (
"syscall"
"time"
"git.eeqj.de/sneak/webhooker/internal/config"
"git.eeqj.de/sneak/webhooker/internal/globals"
"git.eeqj.de/sneak/webhooker/internal/handlers"
"git.eeqj.de/sneak/webhooker/internal/logger"
"git.eeqj.de/sneak/webhooker/internal/middleware"
"go.uber.org/fx"
"sneak.berlin/go/webhooker/internal/config"
"sneak.berlin/go/webhooker/internal/globals"
"sneak.berlin/go/webhooker/internal/handlers"
"sneak.berlin/go/webhooker/internal/logger"
"sneak.berlin/go/webhooker/internal/middleware"
"github.com/getsentry/sentry-go"
"github.com/go-chi/chi"

View File

@ -6,10 +6,10 @@ import (
"log/slog"
"net/http"
"git.eeqj.de/sneak/webhooker/internal/config"
"git.eeqj.de/sneak/webhooker/internal/logger"
"github.com/gorilla/sessions"
"go.uber.org/fx"
"sneak.berlin/go/webhooker/internal/config"
"sneak.berlin/go/webhooker/internal/logger"
)
const (

View File

@ -15,7 +15,7 @@
//
// Usage:
//
// import "git.eeqj.de/sneak/webhooker/pkg/config"
// import "sneak.berlin/go/webhooker/pkg/config"
//
// // Set the environment explicitly
// config.SetEnvironment("prod")

View File

@ -4,8 +4,8 @@ import (
"fmt"
"testing"
"git.eeqj.de/sneak/webhooker/pkg/config"
"github.com/spf13/afero"
"sneak.berlin/go/webhooker/pkg/config"
)
// ExampleSetFs demonstrates how to use an in-memory filesystem for testing

View File

@ -5,7 +5,7 @@ import (
"log"
"os"
"git.eeqj.de/sneak/webhooker/pkg/config"
"sneak.berlin/go/webhooker/pkg/config"
)
func Example() {

View File

@ -1,4 +1,4 @@
module git.eeqj.de/sneak/webhooker/pkg/config
module sneak.berlin/go/webhooker/pkg/config
go 1.23.0