Help us shape the future of Go! Take the 2026 Go Developer Survey

handler

package
v0.2.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 27, 2026 License: AGPL-3.0 Imports: 31 Imported by: 0

Documentation

Index

Constants

View Source
const TimeFormat = time.RFC3339

TimeFormat is the wire format for all timestamps returned by the API.

Variables

This section is empty.

Functions

func CascadeDeleteUser

func CascadeDeleteUser(
	ctx context.Context,
	queries *store.Queries,
	cfg AccountConfig,
	uid uuid.UUID,
	email string,
	blockRecreate bool,
) error

CascadeDeleteUser permanently (hard) deletes a user. Chats, documents and private flows cascade away; usage/payments are anonymised (ON DELETE SET NULL); published flows are handed to the anonymous sentinel author. cfg. PreDelete settles money first (its failure aborts the delete so no charge is orphaned). When blockRecreate is set the email enters the 30-day cooldown, unless cfg.CooldownExempt clears it.

func HashEmail

func HashEmail(email string) string

HashEmail returns the lowercase-email SHA-256 hex digest used for GDPR-safe access/payment logging.

func ModelsHandler

func ModelsHandler(w http.ResponseWriter, r *http.Request)

func NewHealthHandler

func NewHealthHandler(pool *pgxpool.Pool) http.HandlerFunc

func OptionalUserUUID

func OptionalUserUUID(r *http.Request) (uuid.UUID, bool)

OptionalUserUUID returns the authenticated user id if present.

func ParseURLParam

func ParseURLParam(r *http.Request, param string) (uuid.UUID, error)

ParseURLParam parses a chi URL parameter as a UUID.

func ParseUUIDParamOr400

func ParseUUIDParamOr400(w http.ResponseWriter, r *http.Request, param string) (uuid.UUID, bool)

ParseUUIDParamOr400 parses a chi URL parameter as a UUID or writes a 400.

func ProvisionGuestUser

func ProvisionGuestUser(
	ctx context.Context, queries *store.Queries,
) (uuid.UUID, string, error)

func ProvisionUserDefaults

func ProvisionUserDefaults(
	ctx context.Context,
	queries *store.Queries,
	userID uuid.UUID,
)

func RecordAccessEvent

func RecordAccessEvent(
	ctx context.Context,
	queries *store.Queries,
	userID uuid.UUID,
	email, kind, ip, userAgent string,
	refID uuid.UUID,
)

RecordAccessEvent appends an append-only access-log row tying a user to a real action (login, chat, topup, subscribe) with the caller's IP and user-agent. Best-effort chargeback evidence: failures are logged, never surfaced, and never block the request.

func RequireThreadAccess

func RequireThreadAccess(r *http.Request, queries *store.Queries, threadID, userID uuid.UUID) error

RequireThreadAccess returns an error unless the thread belongs to the user.

func RequireUser

func RequireUser(r *http.Request) (uuid.UUID, error)

RequireUser returns the authenticated user's id, or an error if absent.

func RequireUserOr401

func RequireUserOr401(w http.ResponseWriter, r *http.Request) (uuid.UUID, bool)

RequireUserOr401 returns the authenticated user or writes a 401 and false.

func TSPtr

func TSPtr(t pgtype.Timestamptz) *string

TSPtr returns a formatted-string pointer to a valid timestamp, else nil.

func TextPtr

func TextPtr(t pgtype.Text) *string

TextPtr returns a string pointer to a valid pgtype.Text, else nil.

func UUIDPtr

func UUIDPtr(u pgtype.UUID) *string

UUIDPtr returns a string pointer to a valid pgtype.UUID, else nil.

func WelcomeMessages

func WelcomeMessages(
	w http.ResponseWriter, r *http.Request,
)

WelcomeMessages serves the seeded welcome conversation as a list of messages so anonymous visitors can preview it before any guest provisioning happens. The shape mirrors /api/threads/{id}/messages so the frontend can render it through the same code path.

func WriteJSON

func WriteJSON(w http.ResponseWriter, status int, v any)

WriteJSON encodes v as a JSON response with the given status.

func WriteServerError

func WriteServerError(w http.ResponseWriter, logMsg string, err error)

WriteServerError logs err with logMsg context and returns a generic 500.

Types

type AccountConfig

type AccountConfig struct {
	// PreDelete settles money before a hard delete (hosted: refund refundable
	// credit + cancel subscription). A non-nil error aborts the delete.
	PreDelete func(ctx context.Context, userID uuid.UUID) error
	// CooldownExempt reports whether an email skips the 30-day re-registration
	// cooldown (hosted: admins, so they can recreate and re-inherit admin).
	CooldownExempt func(ctx context.Context, email string) bool
}

AccountConfig injects the wrapper-specific policy the account endpoints need. Both hooks are nil-safe (the self-host default).

type AccountHandler

type AccountHandler struct {
	// contains filtered or unexported fields
}

AccountHandler serves the shared signed-in account endpoints: display name, preferred model, node menu, logout, and account deletion.

func NewAccountHandler

func NewAccountHandler(queries *store.Queries, cfg AccountConfig) *AccountHandler

NewAccountHandler builds the account endpoints.

func (*AccountHandler) DeleteAccount

func (h *AccountHandler) DeleteAccount(w http.ResponseWriter, r *http.Request)

DeleteAccount lets a signed-in user hard-delete their own account after confirming their email.

func (*AccountHandler) GetNodeMenu

func (h *AccountHandler) GetNodeMenu(w http.ResponseWriter, r *http.Request)

GetNodeMenu returns the user's flow-editor node palette.

func (*AccountHandler) GetPreferredModel

func (h *AccountHandler) GetPreferredModel(w http.ResponseWriter, r *http.Request)

GetPreferredModel returns the user's preferred model.

func (*AccountHandler) Logout

func (h *AccountHandler) Logout(w http.ResponseWriter, r *http.Request)

Logout clears the session cookie.

func (*AccountHandler) UpdateDisplayName

func (h *AccountHandler) UpdateDisplayName(w http.ResponseWriter, r *http.Request)

UpdateDisplayName lets a signed-in user rename themselves. The name is trimmed and capped at 40 runes; uniqueness is not required.

func (*AccountHandler) UpdateNodeMenu

func (h *AccountHandler) UpdateNodeMenu(w http.ResponseWriter, r *http.Request)

UpdateNodeMenu replaces the user's flow-editor node palette.

func (*AccountHandler) UpdatePreferredModel

func (h *AccountHandler) UpdatePreferredModel(w http.ResponseWriter, r *http.Request)

UpdatePreferredModel sets the user's preferred model.

type AuditHook

type AuditHook func(r *http.Request, action, target string, before, after []byte)

AuditHook records an admin mutation. Wrappers with an audit log (hosted) supply one; self-host passes nil.

type Billing

type Billing interface {
	// Preflight runs before any LLM work. On success it returns the per-prompt
	// Exec context. On rejection it writes the HTTP response itself and returns
	// ok=false, and the caller returns immediately.
	Preflight(ctx context.Context, w http.ResponseWriter, userID uuid.UUID) (Exec, bool)
}

Billing authorizes and accounts for a chat prompt. The hosted wrapper injects a real budget/credit implementation; self-host injects a no-op. It is the single seam through which money/abuse policy enters the shared Execute path — core carries no billing schema or policy of its own.

type ChatHandler

type ChatHandler struct {
	// contains filtered or unexported fields
}

func NewChatHandler

func NewChatHandler(
	queries *store.Queries,
	pool *pgxpool.Pool,
	cfg coreconfig.HandlerConfig,
) *ChatHandler

func (*ChatHandler) ClearMessages

func (h *ChatHandler) ClearMessages(
	w http.ResponseWriter, r *http.Request,
)

func (*ChatHandler) CreateThread

func (h *ChatHandler) CreateThread(
	w http.ResponseWriter, r *http.Request,
)

func (*ChatHandler) DeleteThread

func (h *ChatHandler) DeleteThread(
	w http.ResponseWriter, r *http.Request,
)

func (*ChatHandler) ListThreads

func (h *ChatHandler) ListThreads(
	w http.ResponseWriter, r *http.Request,
)

func (*ChatHandler) Messages

func (h *ChatHandler) Messages(
	w http.ResponseWriter, r *http.Request,
)

func (*ChatHandler) RenameThread

func (h *ChatHandler) RenameThread(
	w http.ResponseWriter, r *http.Request,
)

func (*ChatHandler) UpdateScroll

func (h *ChatHandler) UpdateScroll(
	w http.ResponseWriter, r *http.Request,
)

type DocumentsHandler

type DocumentsHandler struct {
	// contains filtered or unexported fields
}

func NewDocumentsHandler

func NewDocumentsHandler(queries *store.Queries) *DocumentsHandler

func (*DocumentsHandler) Delete

func (h *DocumentsHandler) Delete(
	w http.ResponseWriter, r *http.Request,
)

func (*DocumentsHandler) List

func (h *DocumentsHandler) List(
	w http.ResponseWriter, r *http.Request,
)

type Exec

type Exec interface {
	// Begin builds the (billed or unbilled) LLM client that records activity
	// against the saved user message; the client is handed to the engine.
	Begin(userID, msgID uuid.UUID) flowengine.LLMClient
	// SearchCharged returns the web-search charge hook, or nil when unbilled.
	SearchCharged() func(ctx context.Context, endpoint string, resultCount int)
	// PromptCap is the billed-EUR per-prompt ceiling; 0 means uncapped.
	PromptCap() float64
	// Guest reports the post-reserve free-prompt count for the guest_llm_calls
	// event; ok=false suppresses the event (non-guest / self-host).
	Guest() (used, limit int32, ok bool)
	// Settle records final cost after execution and returns the total billed
	// (0 when unbilled), used for the done event and credit deduction.
	Settle(ctx context.Context) float64
}

Exec is the per-prompt billing context returned by a passed Preflight.

type FlowsHandler

type FlowsHandler struct {
	// contains filtered or unexported fields
}

func NewFlowsHandler

func NewFlowsHandler(
	queries *store.Queries,
	pool *pgxpool.Pool,
	cfg coreconfig.HandlerConfig,
	billing Billing,
) *FlowsHandler

func (*FlowsHandler) Activate

func (h *FlowsHandler) Activate(
	w http.ResponseWriter, r *http.Request,
)

func (*FlowsHandler) Create

func (h *FlowsHandler) Create(
	w http.ResponseWriter, r *http.Request,
)

func (*FlowsHandler) Delete

func (h *FlowsHandler) Delete(
	w http.ResponseWriter, r *http.Request,
)

func (*FlowsHandler) Execute

func (h *FlowsHandler) Execute(w http.ResponseWriter, r *http.Request)

Execute runs a chat prompt through the target flow, streaming the result as ndjson. All money/abuse policy is delegated to the injected Billing seam; everything else — flow/thread resolution, message persistence, engine run, auto-title — is shared across every wrapper.

func (*FlowsHandler) Get

func (h *FlowsHandler) Get(
	w http.ResponseWriter, r *http.Request,
)

func (*FlowsHandler) List

func (h *FlowsHandler) List(
	w http.ResponseWriter, r *http.Request,
)

func (*FlowsHandler) Publish

func (h *FlowsHandler) Publish(
	w http.ResponseWriter, r *http.Request,
)

func (*FlowsHandler) Reset

func (h *FlowsHandler) Reset(
	w http.ResponseWriter, r *http.Request,
)

func (*FlowsHandler) Unpublish

func (h *FlowsHandler) Unpublish(
	w http.ResponseWriter, r *http.Request,
)

func (*FlowsHandler) Update

func (h *FlowsHandler) Update(
	w http.ResponseWriter, r *http.Request,
)

type IssuesHandler

type IssuesHandler struct {
	// contains filtered or unexported fields
}

func NewIssuesHandler

func NewIssuesHandler(queries *store.Queries) *IssuesHandler

func (*IssuesHandler) Report

func (h *IssuesHandler) Report(
	w http.ResponseWriter, r *http.Request,
)

type MarketplaceHandler

type MarketplaceHandler struct {
	// contains filtered or unexported fields
}

func NewMarketplaceHandler

func NewMarketplaceHandler(queries *store.Queries) *MarketplaceHandler

func (*MarketplaceHandler) Get

func (*MarketplaceHandler) List

func (*MarketplaceHandler) Search

func (h *MarketplaceHandler) Search(
	w http.ResponseWriter, r *http.Request,
)

func (*MarketplaceHandler) Vote

type PricingHook

type PricingHook func(ctx context.Context) (map[string][2]float64, error)

PricingHook fetches per-model pricing overrides for the active providers (e.g. by scraping a provider's pricing page). It is optional and provider-specific, so wrappers inject their own.

type ProviderAdmin

type ProviderAdmin struct {
	// contains filtered or unexported fields
}

ProviderAdmin serves the admin provider-CRUD endpoints. It encrypts keys at rest, never returns plaintext, and reloads the live provider set (plus a background model refresh) after every mutation.

func NewProviderAdmin

func NewProviderAdmin(
	queries *store.Queries,
	box *crypto.SecretBox,
	manager *ProviderManager,
	audit AuditHook,
) *ProviderAdmin

NewProviderAdmin builds the handler. audit may be nil.

func (*ProviderAdmin) Create

func (h *ProviderAdmin) Create(w http.ResponseWriter, r *http.Request)

Create adds a provider, encrypting its key.

func (*ProviderAdmin) Delete

func (h *ProviderAdmin) Delete(w http.ResponseWriter, r *http.Request)

Delete removes a provider.

func (*ProviderAdmin) List

func (h *ProviderAdmin) List(w http.ResponseWriter, r *http.Request)

List returns every provider with key metadata but no plaintext.

func (*ProviderAdmin) Update

func (h *ProviderAdmin) Update(w http.ResponseWriter, r *http.Request)

Update edits a provider. An empty api_key leaves the stored key unchanged; the name is immutable (it keys model routing).

type ProviderManager

type ProviderManager struct {
	// contains filtered or unexported fields
}

ProviderManager owns the live LLM-provider set: it rebuilds it from the encrypted DB rows and re-discovers each provider's models. box may be nil when no encryption key is configured, in which case rows carrying a stored key are skipped.

func NewProviderManager

func NewProviderManager(
	queries *store.Queries,
	box *crypto.SecretBox,
	pricingHook PricingHook,
) *ProviderManager

NewProviderManager builds the manager. pricingHook may be nil.

func (*ProviderManager) Refresh

func (m *ProviderManager) Refresh(ctx context.Context)

Refresh re-discovers models from every active provider and rebuilds the catalog. Safe to call on demand after an admin mutates providers. On id collisions the first provider wins.

func (*ProviderManager) Reload

func (m *ProviderManager) Reload(ctx context.Context) error

Reload rebuilds the active provider list purely from the DB. It is the single source of truth: nothing comes from the environment, so an admin fully controls providers at runtime.

func (*ProviderManager) StartModelRefresh

func (m *ProviderManager) StartModelRefresh(ctx context.Context)

StartModelRefresh discovers models once, then re-discovers on a fixed interval until ctx is cancelled.

type UploadHandler

type UploadHandler struct {
	// contains filtered or unexported fields
}

func NewUploadHandler

func NewUploadHandler(
	queries *store.Queries,
) *UploadHandler

func (*UploadHandler) Upload

func (h *UploadHandler) Upload(
	w http.ResponseWriter, r *http.Request,
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL