Add Authentik SSO, budget visibility, Resend email, 12h item default
Opens Veola up to more users (Parodia's Authentik) and makes Apify spend visible to everyone. - Auth: Traefik forward-auth (trust X-Authentik-* headers only from trusted_proxies CIDRs, keyed by email, role synced from admin_group), keeping local password login as break-glass. New [auth] config, CaptureDirectIP + ForwardAuth middleware, deploy/authentik-forward-auth.md. - Budget: count every Apify run (apify_api_usage table) and show calls + estimated cost to all users on the dashboard, with an optional monthly-budget bar. New [budget] config + settings. - Email: Resend client for opt-in deal alerts and a weekly digest (Mon 09:00). Per-user email + toggles on Settings. New [resend] config. - Defaults: new items default to a 12-hour poll interval to cut spend. users table gains email/auth_source/email-pref columns (migrated in place). go build/vet/test green; boots and migrates cleanly.
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
@@ -12,12 +13,53 @@ import (
|
||||
type Config struct {
|
||||
Server ServerConfig `toml:"server"`
|
||||
Security SecurityConfig `toml:"security"`
|
||||
Auth AuthConfig `toml:"auth"`
|
||||
Apify ApifyConfig `toml:"apify"`
|
||||
Ebay EbayConfig `toml:"ebay"`
|
||||
Ntfy NtfyConfig `toml:"ntfy"`
|
||||
Resend ResendConfig `toml:"resend"`
|
||||
Budget BudgetConfig `toml:"budget"`
|
||||
Scheduler SchedulerConfig `toml:"scheduler"`
|
||||
}
|
||||
|
||||
// AuthConfig controls how identities are established. Mode "local" (default)
|
||||
// uses Veola's own password login. Mode "forward" trusts identity headers set
|
||||
// by a reverse proxy doing forward-auth (Traefik in front of Authentik); local
|
||||
// password login stays available as a break-glass fallback. The forward-auth
|
||||
// headers are honored ONLY when the direct connection comes from a CIDR in
|
||||
// TrustedProxies — without that gate anyone reaching the port could spoof them.
|
||||
type AuthConfig struct {
|
||||
Mode string `toml:"mode"`
|
||||
ForwardHeaderUser string `toml:"forward_header_user"`
|
||||
ForwardHeaderEmail string `toml:"forward_header_email"`
|
||||
ForwardHeaderName string `toml:"forward_header_name"`
|
||||
ForwardHeaderGroups string `toml:"forward_header_groups"`
|
||||
AdminGroup string `toml:"admin_group"`
|
||||
TrustedProxies []string `toml:"trusted_proxies"`
|
||||
}
|
||||
|
||||
// ForwardAuthEnabled reports whether forward-auth header trust is active.
|
||||
func (c AuthConfig) ForwardAuthEnabled() bool {
|
||||
return strings.EqualFold(strings.TrimSpace(c.Mode), "forward")
|
||||
}
|
||||
|
||||
// ResendConfig holds Resend transactional-email credentials. APIKey and From
|
||||
// can both be overridden at runtime via /settings (resend_api_key,
|
||||
// resend_from). From is an RFC-5322 address, e.g. "Veola <veola@example.com>".
|
||||
type ResendConfig struct {
|
||||
APIKey string `toml:"api_key"`
|
||||
From string `toml:"from"`
|
||||
}
|
||||
|
||||
// BudgetConfig drives the cost estimate shown to every user. CostPerApifyCall
|
||||
// is a USD estimate multiplied by the recorded Apify run count (Apify does not
|
||||
// report exact spend back to Veola). MonthlyBudgetUSD, when > 0, draws a
|
||||
// budget-consumed bar on the dashboard. Both are overridable via /settings.
|
||||
type BudgetConfig struct {
|
||||
CostPerApifyCall float64 `toml:"apify_cost_per_call"`
|
||||
MonthlyBudgetUSD float64 `toml:"monthly_budget_usd"`
|
||||
}
|
||||
|
||||
// EbayConfig holds credentials for eBay's official Buy > Browse API. When set,
|
||||
// eBay marketplaces are polled through the Browse API instead of an Apify
|
||||
// scraper actor. ClientID is the App ID and ClientSecret is the Cert ID from
|
||||
@@ -144,6 +186,26 @@ func (c *Config) validate() error {
|
||||
if c.Ebay.DailyCallLimit == 0 {
|
||||
c.Ebay.DailyCallLimit = 5000
|
||||
}
|
||||
// Default the forward-auth header names to Authentik's defaults so a
|
||||
// minimal [auth] block (just mode + trusted_proxies) works out of the box.
|
||||
if c.Auth.ForwardHeaderUser == "" {
|
||||
c.Auth.ForwardHeaderUser = "X-Authentik-Username"
|
||||
}
|
||||
if c.Auth.ForwardHeaderEmail == "" {
|
||||
c.Auth.ForwardHeaderEmail = "X-Authentik-Email"
|
||||
}
|
||||
if c.Auth.ForwardHeaderName == "" {
|
||||
c.Auth.ForwardHeaderName = "X-Authentik-Name"
|
||||
}
|
||||
if c.Auth.ForwardHeaderGroups == "" {
|
||||
c.Auth.ForwardHeaderGroups = "X-Authentik-Groups"
|
||||
}
|
||||
if c.Auth.AdminGroup == "" {
|
||||
c.Auth.AdminGroup = "veola-admins"
|
||||
}
|
||||
if c.Auth.ForwardAuthEnabled() && len(c.Auth.TrustedProxies) == 0 {
|
||||
return errors.New("auth.mode is \"forward\" but auth.trusted_proxies is empty: forward-auth headers would be spoofable. Set the CIDR(s) of your reverse proxy (e.g. [\"127.0.0.1/32\"])")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user