Harden forward-auth, per-user isolation, and budget accuracy
Code-review fixes on the Authentik/budget/Resend work: - UpsertForwardUser: avoid 500 lockout on username UNIQUE collision and on the concurrent first-login race; normalize emails (lower+trim) so case variants don't create duplicate accounts. - Centralize email management in Authentik: when forward-auth mode is on, email is read-only in Veola for every user (not just forward rows); pure-local deployments keep editable email for Resend. - Fail closed on per-user isolation: ownedItem rejects uid==0/orphaned items; dashboard and global results error on a userless request. - Budget accuracy: count Apify usage only after a successful run, and count the billed Test Apify run. - Efficiency: resolve the deal-email recipient once per poll; build the settings page once in PostEmailPrefs.
This commit is contained in:
@@ -110,7 +110,7 @@ func (m *Manager) ForwardAuth(next http.Handler) http.Handler {
|
|||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
email := strings.TrimSpace(r.Header.Get(fa.HeaderEmail))
|
email := strings.ToLower(strings.TrimSpace(r.Header.Get(fa.HeaderEmail)))
|
||||||
if email == "" {
|
if email == "" {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -142,7 +142,16 @@ func (s *Store) GetUserByID(ctx context.Context, id int64) (*models.User, error)
|
|||||||
return u, err
|
return u, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// normalizeEmail lowercases and trims an address so identity matching is
|
||||||
|
// case-insensitive. The users.email unique index and the forward-auth match key
|
||||||
|
// are otherwise byte-exact, which would let "Ada@x.com" and "ada@x.com" become
|
||||||
|
// two accounts for one person.
|
||||||
|
func normalizeEmail(email string) string {
|
||||||
|
return strings.ToLower(strings.TrimSpace(email))
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Store) GetUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
func (s *Store) GetUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
||||||
|
email = normalizeEmail(email)
|
||||||
if email == "" {
|
if email == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -176,6 +185,7 @@ func (s *Store) ListUsers(ctx context.Context) ([]models.User, error) {
|
|||||||
// auth_source='forward' and carry an unusable password hash (forward-auth
|
// auth_source='forward' and carry an unusable password hash (forward-auth
|
||||||
// users never log in with a local password).
|
// users never log in with a local password).
|
||||||
func (s *Store) UpsertForwardUser(ctx context.Context, email, username string, role models.Role) (*models.User, error) {
|
func (s *Store) UpsertForwardUser(ctx context.Context, email, username string, role models.Role) (*models.User, error) {
|
||||||
|
email = normalizeEmail(email)
|
||||||
if email == "" {
|
if email == "" {
|
||||||
return nil, errors.New("forward-auth requires an email")
|
return nil, errors.New("forward-auth requires an email")
|
||||||
}
|
}
|
||||||
@@ -184,11 +194,17 @@ func (s *Store) UpsertForwardUser(ctx context.Context, email, username string, r
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if existing != nil {
|
if existing != nil {
|
||||||
if existing.Role != role || (username != "" && existing.Username != username) {
|
|
||||||
newName := existing.Username
|
newName := existing.Username
|
||||||
if username != "" {
|
if username != "" && username != existing.Username {
|
||||||
newName = username
|
// The IdP renamed the user. users.username is UNIQUE, so the
|
||||||
|
// desired name may already belong to a different row; resolve a
|
||||||
|
// free variant rather than letting the UPDATE fail with a 500.
|
||||||
|
newName, err = s.uniqueUsername(ctx, username, existing.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if existing.Role != role || newName != existing.Username {
|
||||||
if _, err := s.DB.ExecContext(ctx,
|
if _, err := s.DB.ExecContext(ctx,
|
||||||
`UPDATE users SET role = ?, username = ? WHERE id = ?`,
|
`UPDATE users SET role = ?, username = ? WHERE id = ?`,
|
||||||
string(role), newName, existing.ID); err != nil {
|
string(role), newName, existing.ID); err != nil {
|
||||||
@@ -199,13 +215,27 @@ func (s *Store) UpsertForwardUser(ctx context.Context, email, username string, r
|
|||||||
}
|
}
|
||||||
return existing, nil
|
return existing, nil
|
||||||
}
|
}
|
||||||
if username == "" {
|
desired := username
|
||||||
username = email
|
if desired == "" {
|
||||||
|
desired = email
|
||||||
|
}
|
||||||
|
// users.username is UNIQUE; an Authentik username can collide with an
|
||||||
|
// existing local/forward row. Pick a free variant so a new identity is
|
||||||
|
// never locked out by a 500 on a username clash.
|
||||||
|
uname, err := s.uniqueUsername(ctx, desired, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
res, err := s.DB.ExecContext(ctx,
|
res, err := s.DB.ExecContext(ctx,
|
||||||
`INSERT INTO users (username, password_hash, role, email, auth_source) VALUES (?, ?, ?, ?, 'forward')`,
|
`INSERT INTO users (username, password_hash, role, email, auth_source) VALUES (?, ?, ?, ?, 'forward')`,
|
||||||
username, "!forward-auth", string(role), email)
|
uname, "!forward-auth", string(role), email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// A concurrent first-login for the same email may have created the row
|
||||||
|
// between GetUserByEmail and this INSERT; the email unique index then
|
||||||
|
// rejects us. Re-resolve and let the race winner stand.
|
||||||
|
if u, getErr := s.GetUserByEmail(ctx, email); getErr == nil && u != nil {
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
id, err := res.LastInsertId()
|
id, err := res.LastInsertId()
|
||||||
@@ -215,6 +245,24 @@ func (s *Store) UpsertForwardUser(ctx context.Context, email, username string, r
|
|||||||
return s.GetUserByID(ctx, id)
|
return s.GetUserByID(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uniqueUsername returns desired if it is free (or already held by excludeID),
|
||||||
|
// otherwise appends a numeric suffix until a free name is found. Bounded so a
|
||||||
|
// pathological collision set can't loop forever.
|
||||||
|
func (s *Store) uniqueUsername(ctx context.Context, desired string, excludeID int64) (string, error) {
|
||||||
|
candidate := desired
|
||||||
|
for i := 1; i <= 1000; i++ {
|
||||||
|
ex, err := s.GetUserByUsername(ctx, candidate)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if ex == nil || ex.ID == excludeID {
|
||||||
|
return candidate, nil
|
||||||
|
}
|
||||||
|
candidate = fmt.Sprintf("%s-%d", desired, i)
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("no free username for %q", desired)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Store) UpdateUserPassword(ctx context.Context, id int64, hash string) error {
|
func (s *Store) UpdateUserPassword(ctx context.Context, id int64, hash string) error {
|
||||||
_, err := s.DB.ExecContext(ctx, `UPDATE users SET password_hash = ? WHERE id = ?`, hash, id)
|
_, err := s.DB.ExecContext(ctx, `UPDATE users SET password_hash = ? WHERE id = ?`, hash, id)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"veola/internal/db"
|
"veola/internal/db"
|
||||||
@@ -31,6 +32,11 @@ func (a *App) GetDashboardRefresh(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (a *App) dashboardData(r *http.Request) (templates.DashboardData, error) {
|
func (a *App) dashboardData(r *http.Request) (templates.DashboardData, error) {
|
||||||
uid := a.userID(r)
|
uid := a.userID(r)
|
||||||
|
// Fail closed: every store method below treats ownerID 0 as "all owners",
|
||||||
|
// so a userless request must error rather than render cross-tenant data.
|
||||||
|
if uid == 0 {
|
||||||
|
return templates.DashboardData{}, errors.New("dashboard: no authenticated user in context")
|
||||||
|
}
|
||||||
stats, err := a.Store.GetDashboardStatsForUser(r.Context(), uid)
|
stats, err := a.Store.GetDashboardStatsForUser(r.Context(), uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return templates.DashboardData{}, err
|
return templates.DashboardData{}, err
|
||||||
|
|||||||
@@ -28,7 +28,10 @@ func (a *App) ownedItem(r *http.Request, id int64) (*models.Item, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if it == nil || it.UserID != a.userID(r) {
|
// Fail closed: a zero uid (no authenticated user) must never match, and an
|
||||||
|
// orphaned item (UserID 0) must never be treated as owned-by-default.
|
||||||
|
uid := a.userID(r)
|
||||||
|
if it == nil || uid == 0 || it.UserID != uid {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return it, nil
|
return it, nil
|
||||||
|
|||||||
@@ -106,6 +106,12 @@ func (a *App) GetGlobalResults(w http.ResponseWriter, r *http.Request) {
|
|||||||
to := strings.TrimSpace(q.Get("to"))
|
to := strings.TrimSpace(q.Get("to"))
|
||||||
|
|
||||||
uid := a.userID(r)
|
uid := a.userID(r)
|
||||||
|
// Fail closed: ListResults/NextEndingResult treat OwnerID 0 as "all owners",
|
||||||
|
// so a userless request must not fall through to a cross-tenant view.
|
||||||
|
if uid == 0 {
|
||||||
|
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
items, err := a.Store.ListItemsForUser(r.Context(), uid)
|
items, err := a.Store.ListItemsForUser(r.Context(), uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.serverError(w, r, err)
|
a.serverError(w, r, err)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -91,6 +92,7 @@ func (a *App) settingsData(r *http.Request) (templates.SettingsData, error) {
|
|||||||
ApifyMonth: apifyMonth,
|
ApifyMonth: apifyMonth,
|
||||||
ApifyCostPerCall: costPerCall,
|
ApifyCostPerCall: costPerCall,
|
||||||
MonthlyBudget: monthlyBudget,
|
MonthlyBudget: monthlyBudget,
|
||||||
|
ForwardAuthMode: a.Cfg.Auth.ForwardAuthEnabled(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,11 +110,13 @@ func (a *App) PostEmailPrefs(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
email := strings.TrimSpace(r.PostFormValue("email"))
|
email := strings.TrimSpace(r.PostFormValue("email"))
|
||||||
// Forward-auth (Authentik) owns the address: it is the IdP match key, kept
|
// In forward-auth mode, Authentik is the single source of truth for email
|
||||||
// in sync on every request. Never overwrite it from the form, or the next
|
// addresses — Veola never writes them. For a forward row this also preserves
|
||||||
// sign-in would no longer match and orphan the row. Users change their
|
// the IdP match key (changing it would orphan the row on next sign-in); for
|
||||||
// email in Authentik; only the opt-in toggles are editable here.
|
// a break-glass local row it keeps email management centralized in Authentik.
|
||||||
if cur.AuthSource == "forward" {
|
// Only the opt-in toggles are editable here. When forward-auth is off (pure
|
||||||
|
// local deployment, no IdP), local users set their own Resend address.
|
||||||
|
if a.Cfg.Auth.ForwardAuthEnabled() || cur.AuthSource == "forward" {
|
||||||
email = cur.Email
|
email = cur.Email
|
||||||
}
|
}
|
||||||
dealAlerts := r.PostFormValue("email_deal_alerts") == "1"
|
dealAlerts := r.PostFormValue("email_deal_alerts") == "1"
|
||||||
@@ -132,12 +136,8 @@ func (a *App) PostEmailPrefs(w http.ResponseWriter, r *http.Request) {
|
|||||||
a.serverError(w, r, err)
|
a.serverError(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Reflect the saved values immediately (CurrentUser in d predates the write).
|
// Reflect the saved values immediately on the already-built data (CurrentUser
|
||||||
d, err = a.settingsData(r)
|
// in d predates the write); no need to reassemble the whole settings page.
|
||||||
if err != nil {
|
|
||||||
a.serverError(w, r, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if d.Page.CurrentUser != nil {
|
if d.Page.CurrentUser != nil {
|
||||||
d.Page.CurrentUser.Email = email
|
d.Page.CurrentUser.Email = email
|
||||||
d.Page.CurrentUser.EmailDealAlerts = dealAlerts
|
d.Page.CurrentUser.EmailDealAlerts = dealAlerts
|
||||||
@@ -347,6 +347,11 @@ func (a *App) PostTestApify(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
d.TestApifyOK = "Apify test failed: " + err.Error()
|
d.TestApifyOK = "Apify test failed: " + err.Error()
|
||||||
} else {
|
} else {
|
||||||
|
// The test launches a real, billed actor run; count it toward the
|
||||||
|
// budget view like every other Apify run.
|
||||||
|
if incErr := a.Store.IncrementApifyUsage(r.Context()); incErr != nil {
|
||||||
|
slog.Error("apify usage increment failed", "err", incErr)
|
||||||
|
}
|
||||||
d.TestApifyOK = fmt.Sprintf("Apify returned %d item(s).", len(raw))
|
d.TestApifyOK = fmt.Sprintf("Apify returned %d item(s).", len(raw))
|
||||||
}
|
}
|
||||||
render(w, r, templates.Settings(d))
|
render(w, r, templates.Settings(d))
|
||||||
|
|||||||
@@ -28,26 +28,31 @@ func (s *Scheduler) emailClient(ctx context.Context) *email.Client {
|
|||||||
return email.New(apiKey, from)
|
return email.New(apiKey, from)
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendDealEmails mails a deal alert to the item's owner, if that user opted
|
// dealMailer resolves the item owner and Resend client for deal-alert email.
|
||||||
// into deal-alert email and has a destination address. Items are private, so a
|
// It returns (nil, nil) when email should be skipped (Resend unconfigured, no
|
||||||
// deal only ever notifies the person who is watching it. Entirely best-effort:
|
// owner, owner opted out, or no address) so the caller can resolve once per
|
||||||
// an unconfigured Resend account or a send failure is logged and swallowed.
|
// poll instead of once per result. Best-effort: errors are logged and swallowed.
|
||||||
func (s *Scheduler) sendDealEmails(ctx context.Context, it models.Item, r apify.UnifiedResult) {
|
func (s *Scheduler) dealMailer(ctx context.Context, it models.Item) (*email.Client, *models.User) {
|
||||||
client := s.emailClient(ctx)
|
client := s.emailClient(ctx)
|
||||||
if !client.Configured() {
|
if !client.Configured() || it.UserID == 0 {
|
||||||
return
|
return nil, nil
|
||||||
}
|
|
||||||
if it.UserID == 0 {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
owner, err := s.store.GetUserByID(ctx, it.UserID)
|
owner, err := s.store.GetUserByID(ctx, it.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("load item owner failed", "item_id", it.ID, "err", err)
|
slog.Error("load item owner failed", "item_id", it.ID, "err", err)
|
||||||
return
|
return nil, nil
|
||||||
}
|
}
|
||||||
if owner == nil || !owner.EmailDealAlerts || owner.Email == "" {
|
if owner == nil || !owner.EmailDealAlerts || owner.Email == "" {
|
||||||
return
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
return client, owner
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendDealEmail mails a single deal alert to the resolved owner. The client and
|
||||||
|
// owner come from dealMailer (resolved once per poll), so this does no DB or
|
||||||
|
// settings I/O per result. Items are private, so a deal only ever notifies the
|
||||||
|
// person watching it.
|
||||||
|
func (s *Scheduler) sendDealEmail(ctx context.Context, client *email.Client, owner *models.User, it models.Item, r apify.UnifiedResult) {
|
||||||
subject := fmt.Sprintf("Veola deal: %s", it.Name)
|
subject := fmt.Sprintf("Veola deal: %s", it.Name)
|
||||||
price := fmt.Sprintf("%s%.2f", currencyPrefix(r.Currency), r.Price)
|
price := fmt.Sprintf("%s%.2f", currencyPrefix(r.Currency), r.Price)
|
||||||
target := ""
|
target := ""
|
||||||
|
|||||||
@@ -221,6 +221,10 @@ func (s *Scheduler) RunPoll(ctx context.Context, it models.Item) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
bestIdx := PickBest(results)
|
bestIdx := PickBest(results)
|
||||||
|
// Resolve the deal-email recipient once: owner and Resend client are
|
||||||
|
// constant for the whole poll, so per-result sends reuse them instead of
|
||||||
|
// re-reading settings and the owner row for every alerting result.
|
||||||
|
dealClient, dealOwner := s.dealMailer(ctx, it)
|
||||||
alertsSent := 0
|
alertsSent := 0
|
||||||
for _, r := range results {
|
for _, r := range results {
|
||||||
exists, err := s.store.ResultExists(ctx, it.ID, r.URL)
|
exists, err := s.store.ResultExists(ctx, it.ID, r.URL)
|
||||||
@@ -247,9 +251,11 @@ func (s *Scheduler) RunPoll(ctx context.Context, it models.Item) {
|
|||||||
alerted = true
|
alerted = true
|
||||||
alertsSent++
|
alertsSent++
|
||||||
}
|
}
|
||||||
// Email opted-in users in addition to ntfy. Best-effort: a mail
|
// Email the owner in addition to ntfy. Best-effort: a mail failure
|
||||||
// failure must not block result storage or the next alert.
|
// must not block result storage or the next alert.
|
||||||
s.sendDealEmails(ctx, it, r)
|
if dealOwner != nil {
|
||||||
|
s.sendDealEmail(ctx, dealClient, dealOwner, it, r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
price := r.Price
|
price := r.Price
|
||||||
_, err = s.store.InsertResult(ctx, &models.Result{
|
_, err = s.store.InsertResult(ctx, &models.Result{
|
||||||
@@ -323,10 +329,17 @@ func (s *Scheduler) apifyClient(ctx context.Context) *apify.Client {
|
|||||||
// usage counter that feeds the budget view. Every Apify-billed run in Veola
|
// usage counter that feeds the budget view. Every Apify-billed run in Veola
|
||||||
// goes through here so the count stays honest.
|
// goes through here so the count stays honest.
|
||||||
func (s *Scheduler) runApifyActor(ctx context.Context, actorID string, input any) ([]json.RawMessage, error) {
|
func (s *Scheduler) runApifyActor(ctx context.Context, actorID string, input any) ([]json.RawMessage, error) {
|
||||||
|
raw, err := s.apifyClient(ctx).Run(ctx, actorID, input)
|
||||||
|
if err != nil {
|
||||||
|
// Count only runs that actually reached Apify. Counting before the call
|
||||||
|
// would inflate the budget estimate with config errors and failed runs
|
||||||
|
// that Apify never billed.
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if err := s.store.IncrementApifyUsage(ctx); err != nil {
|
if err := s.store.IncrementApifyUsage(ctx); err != nil {
|
||||||
slog.Error("apify usage increment failed", "err", err)
|
slog.Error("apify usage increment failed", "err", err)
|
||||||
}
|
}
|
||||||
return s.apifyClient(ctx).Run(ctx, actorID, input)
|
return raw, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApifyUsage reports Apify run counts and the cost model used by the budget
|
// ApifyUsage reports Apify run counts and the cost model used by the budget
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ type SettingsData struct {
|
|||||||
UserError string
|
UserError string
|
||||||
EmailMsg string
|
EmailMsg string
|
||||||
EmailError string
|
EmailError string
|
||||||
|
// ForwardAuthMode is true when the deployment delegates identity to
|
||||||
|
// Authentik (forward-auth). Email is then managed centrally in Authentik
|
||||||
|
// and read-only in Veola for every user.
|
||||||
|
ForwardAuthMode bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApifyMonthCost is the estimated month-to-date Apify spend.
|
// ApifyMonthCost is the estimated month-to-date Apify spend.
|
||||||
@@ -302,11 +306,12 @@ func currentWeeklyDigest(d SettingsData) bool {
|
|||||||
return d.CurrentUser != nil && d.CurrentUser.EmailWeeklyDigest
|
return d.CurrentUser != nil && d.CurrentUser.EmailWeeklyDigest
|
||||||
}
|
}
|
||||||
|
|
||||||
// emailManagedExternally reports whether the signed-in user's email comes from
|
// emailManagedExternally reports whether email is owned by Authentik and so is
|
||||||
// an identity provider (Authentik forward-auth) and so is read-only here. The
|
// read-only in Veola. True for any user when the deployment runs in forward-auth
|
||||||
// address is the IdP match key; letting them edit it would orphan the row.
|
// mode (email is centralized in Authentik), and for an individual forward row
|
||||||
|
// regardless of mode (the address is its IdP match key).
|
||||||
func emailManagedExternally(d SettingsData) bool {
|
func emailManagedExternally(d SettingsData) bool {
|
||||||
return d.CurrentUser != nil && d.CurrentUser.AuthSource == "forward"
|
return d.ForwardAuthMode || (d.CurrentUser != nil && d.CurrentUser.AuthSource == "forward")
|
||||||
}
|
}
|
||||||
|
|
||||||
templ Settings(d SettingsData) {
|
templ Settings(d SettingsData) {
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ type SettingsData struct {
|
|||||||
UserError string
|
UserError string
|
||||||
EmailMsg string
|
EmailMsg string
|
||||||
EmailError string
|
EmailError string
|
||||||
|
// ForwardAuthMode is true when the deployment delegates identity to
|
||||||
|
// Authentik (forward-auth). Email is then managed centrally in Authentik
|
||||||
|
// and read-only in Veola for every user.
|
||||||
|
ForwardAuthMode bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApifyMonthCost is the estimated month-to-date Apify spend.
|
// ApifyMonthCost is the estimated month-to-date Apify spend.
|
||||||
@@ -83,7 +87,7 @@ func credStatus(d SettingsData, key string) templ.Component {
|
|||||||
var templ_7745c5c3_Var2 string
|
var templ_7745c5c3_Var2 string
|
||||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(s)
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(s)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 52, Col: 14}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 56, Col: 14}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -158,7 +162,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var4 string
|
var templ_7745c5c3_Var4 string
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["ebay_daily_call_limit"])
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["ebay_daily_call_limit"])
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 85, Col: 122}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 89, Col: 122}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -176,7 +180,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var5 string
|
var templ_7745c5c3_Var5 string
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d / %d", d.EbayUsedToday, d.EbayDailyLimit))
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d / %d", d.EbayUsedToday, d.EbayDailyLimit))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 90, Col: 89}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 94, Col: 89}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -194,7 +198,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var6 string
|
var templ_7745c5c3_Var6 string
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d (uncapped)", d.EbayUsedToday))
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d (uncapped)", d.EbayUsedToday))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 92, Col: 77}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 96, Col: 77}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -218,7 +222,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var7 string
|
var templ_7745c5c3_Var7 string
|
||||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["ntfy_base_url"])
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["ntfy_base_url"])
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 100, Col: 82}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 104, Col: 82}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -231,7 +235,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var8 string
|
var templ_7745c5c3_Var8 string
|
||||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["ntfy_default_topic"])
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["ntfy_default_topic"])
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 104, Col: 92}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 108, Col: 92}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -252,7 +256,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var9 string
|
var templ_7745c5c3_Var9 string
|
||||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["global_poll_interval_minutes"])
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["global_poll_interval_minutes"])
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 113, Col: 136}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 117, Col: 136}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -265,7 +269,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var10 string
|
var templ_7745c5c3_Var10 string
|
||||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["match_confidence_threshold"])
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["match_confidence_threshold"])
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 117, Col: 160}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 121, Col: 160}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -286,7 +290,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var11 string
|
var templ_7745c5c3_Var11 string
|
||||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["resend_from"])
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["resend_from"])
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 126, Col: 78}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 130, Col: 78}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -299,7 +303,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var12 string
|
var templ_7745c5c3_Var12 string
|
||||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["apify_cost_per_call"])
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["apify_cost_per_call"])
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 131, Col: 139}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 135, Col: 139}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -312,7 +316,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var13 string
|
var templ_7745c5c3_Var13 string
|
||||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["monthly_budget_usd"])
|
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.Values["monthly_budget_usd"])
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 136, Col: 133}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 140, Col: 133}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -345,7 +349,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var14 string
|
var templ_7745c5c3_Var14 string
|
||||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(d.TestNtfyOK)
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(d.TestNtfyOK)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 152, Col: 44}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 156, Col: 44}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -364,7 +368,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var15 string
|
var templ_7745c5c3_Var15 string
|
||||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(d.TestApifyOK)
|
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(d.TestApifyOK)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 155, Col: 45}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 159, Col: 45}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -383,7 +387,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var16 string
|
var templ_7745c5c3_Var16 string
|
||||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(d.TestEbayOK)
|
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(d.TestEbayOK)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 158, Col: 44}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 162, Col: 44}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -402,7 +406,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var17 string
|
var templ_7745c5c3_Var17 string
|
||||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(d.TestResendOK)
|
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(d.TestResendOK)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 161, Col: 46}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 165, Col: 46}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -420,7 +424,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var18 string
|
var templ_7745c5c3_Var18 string
|
||||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", d.ApifyMonth))
|
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", d.ApifyMonth))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 165, Col: 61}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 169, Col: 61}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -438,7 +442,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var19 string
|
var templ_7745c5c3_Var19 string
|
||||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("(~$%.2f est.)", d.ApifyMonthCost()))
|
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("(~$%.2f est.)", d.ApifyMonthCost()))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 167, Col: 82}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 171, Col: 82}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -456,7 +460,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var20 string
|
var templ_7745c5c3_Var20 string
|
||||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", d.ApifyToday))
|
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", d.ApifyToday))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 170, Col: 61}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 174, Col: 61}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -474,7 +478,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var21 string
|
var templ_7745c5c3_Var21 string
|
||||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(d.PasswordError)
|
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(d.PasswordError)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 177, Col: 48}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 181, Col: 48}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -493,7 +497,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var22 string
|
var templ_7745c5c3_Var22 string
|
||||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(d.PasswordMsg)
|
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(d.PasswordMsg)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 180, Col: 40}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 184, Col: 40}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -524,7 +528,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var23 string
|
var templ_7745c5c3_Var23 string
|
||||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(d.EmailError)
|
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(d.EmailError)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 203, Col: 45}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 207, Col: 45}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -543,7 +547,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var24 string
|
var templ_7745c5c3_Var24 string
|
||||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(d.EmailMsg)
|
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(d.EmailMsg)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 206, Col: 37}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 210, Col: 37}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -574,7 +578,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var25 string
|
var templ_7745c5c3_Var25 string
|
||||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.ResolveAttributeValue(currentEmail(d))
|
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.ResolveAttributeValue(currentEmail(d))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 213, Col: 65}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 217, Col: 65}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var25)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var25)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -592,7 +596,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var26 string
|
var templ_7745c5c3_Var26 string
|
||||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.ResolveAttributeValue(currentEmail(d))
|
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.ResolveAttributeValue(currentEmail(d))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 216, Col: 78}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 220, Col: 78}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var26)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var26)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -640,7 +644,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var27 string
|
var templ_7745c5c3_Var27 string
|
||||||
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(d.UserError)
|
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(d.UserError)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 236, Col: 45}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 240, Col: 45}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -659,7 +663,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var28 string
|
var templ_7745c5c3_Var28 string
|
||||||
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(d.UserMsg)
|
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(d.UserMsg)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 239, Col: 37}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 243, Col: 37}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -682,7 +686,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var29 string
|
var templ_7745c5c3_Var29 string
|
||||||
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(u.Username)
|
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(u.Username)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 246, Col: 24}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 250, Col: 24}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -695,7 +699,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var30 string
|
var templ_7745c5c3_Var30 string
|
||||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(string(u.Role))
|
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(string(u.Role))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 247, Col: 44}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 251, Col: 44}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -708,7 +712,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var31 string
|
var templ_7745c5c3_Var31 string
|
||||||
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(u.CreatedAt.Format("2006-01-02"))
|
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(u.CreatedAt.Format("2006-01-02"))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 248, Col: 70}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 252, Col: 70}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -721,7 +725,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var32 templ.SafeURL
|
var templ_7745c5c3_Var32 templ.SafeURL
|
||||||
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/users/%d/reset-password", u.ID)))
|
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/users/%d/reset-password", u.ID)))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 250, Col: 113}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 254, Col: 113}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -734,7 +738,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var33 string
|
var templ_7745c5c3_Var33 string
|
||||||
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.CSRFToken)
|
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.CSRFToken)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 251, Col: 68}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 255, Col: 68}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var33)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var33)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -747,7 +751,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var34 templ.SafeURL
|
var templ_7745c5c3_Var34 templ.SafeURL
|
||||||
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/users/%d/delete", u.ID)))
|
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/users/%d/delete", u.ID)))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 255, Col: 105}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 259, Col: 105}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -760,7 +764,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var35 string
|
var templ_7745c5c3_Var35 string
|
||||||
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.CSRFToken)
|
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.CSRFToken)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 256, Col: 68}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 260, Col: 68}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var35)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var35)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -778,7 +782,7 @@ func settingsBody(d SettingsData) templ.Component {
|
|||||||
var templ_7745c5c3_Var36 string
|
var templ_7745c5c3_Var36 string
|
||||||
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.CSRFToken)
|
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.ResolveAttributeValue(d.CSRFToken)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 265, Col: 63}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/settings.templ`, Line: 269, Col: 63}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var36)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var36)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -814,11 +818,12 @@ func currentWeeklyDigest(d SettingsData) bool {
|
|||||||
return d.CurrentUser != nil && d.CurrentUser.EmailWeeklyDigest
|
return d.CurrentUser != nil && d.CurrentUser.EmailWeeklyDigest
|
||||||
}
|
}
|
||||||
|
|
||||||
// emailManagedExternally reports whether the signed-in user's email comes from
|
// emailManagedExternally reports whether email is owned by Authentik and so is
|
||||||
// an identity provider (Authentik forward-auth) and so is read-only here. The
|
// read-only in Veola. True for any user when the deployment runs in forward-auth
|
||||||
// address is the IdP match key; letting them edit it would orphan the row.
|
// mode (email is centralized in Authentik), and for an individual forward row
|
||||||
|
// regardless of mode (the address is its IdP match key).
|
||||||
func emailManagedExternally(d SettingsData) bool {
|
func emailManagedExternally(d SettingsData) bool {
|
||||||
return d.CurrentUser != nil && d.CurrentUser.AuthSource == "forward"
|
return d.ForwardAuthMode || (d.CurrentUser != nil && d.CurrentUser.AuthSource == "forward")
|
||||||
}
|
}
|
||||||
|
|
||||||
func Settings(d SettingsData) templ.Component {
|
func Settings(d SettingsData) templ.Component {
|
||||||
|
|||||||
Reference in New Issue
Block a user