Files
2026-08-11 10:40:10 +01:00

116 lines
3.8 KiB
Go

package web
import (
"net/http"
"github.com/andydixon/relata/internal/api"
"github.com/andydixon/relata/internal/relationships"
)
// publicBase carries what every public page needs.
//
// It deliberately does not embed `base`: the shared page context reads the
// pending-review count from the database, and a page served to an anonymous
// visitor must touch no workspace data at all.
type publicBase struct {
Title string
Version string
SignedIn bool
OAuthEnabled bool
LocalEnabled bool
ProviderName string
RequestID string
}
func (h *Handlers) newPublicBase(r *http.Request, title string) publicBase {
return publicBase{
Title: title,
Version: h.d.Version,
SignedIn: api.PrincipalFrom(r.Context()) != nil,
OAuthEnabled: h.oauthEnabled(),
LocalEnabled: h.localLoginEnabled(),
ProviderName: h.providerName(),
}
}
// LandingPage is the only dual-purpose route.
//
// Signed in, it is the dashboard. Signed out, it is a description of what
// Relata does — served rather than redirected, so the front page of the site is
// not a login form.
func (h *Handlers) LandingPage(w http.ResponseWriter, r *http.Request) {
if api.PrincipalFrom(r.Context()) != nil {
h.Dashboard(w, r)
return
}
data := struct {
publicBase
Weights []publicWeightRow
}{
publicBase: h.newPublicBase(r, "Relata — a self-organising semantic document database"),
Weights: publicWeightRows(),
}
h.d.Renderer.RenderPublic(w, r, "public_landing.html", http.StatusOK, data)
}
// HowItWorksPage explains the pipeline and the scoring, without any data.
func (h *Handlers) HowItWorksPage(w http.ResponseWriter, r *http.Request) {
data := struct {
publicBase
Weights []publicWeightRow
}{
publicBase: h.newPublicBase(r, "How Relata works"),
Weights: publicWeightRows(),
}
h.d.Renderer.RenderPublic(w, r, "public_how.html", http.StatusOK, data)
}
// PublicAPIPage describes the API and how to authenticate against it.
func (h *Handlers) PublicAPIPage(w http.ResponseWriter, r *http.Request) {
data := struct {
publicBase
CallbackURL string
}{
publicBase: h.newPublicBase(r, "The Relata API"),
CallbackURL: h.callbackURL(),
}
h.d.Renderer.RenderPublic(w, r, "public_api.html", http.StatusOK, data)
}
// publicWeightRow is one signal weight, shown publicly because the scoring model is
// part of what the product claims rather than something to be discovered.
type publicWeightRow struct {
Signal string
Weight float64
Means string
}
func publicWeightRows() []publicWeightRow {
explain := map[string]string{
"content_hash_match": "Byte-identical content.",
"semantic_similarity": "How alike the wording is, rebased so that ordinary similarity counts for little.",
"shared_entity": "An exact entity match, discounted by how common that entity is.",
"claim_support": "Two statements agree about the same subject.",
"claim_contradiction": "Two statements disagree about the same subject.",
"model_classification": "A language model's opinion. One signal, weighted below the storage " +
"threshold, so it can never create a relationship on its own.",
"shared_concept": "Overlapping concept vocabulary.",
"alias_entity": "A match through an alias a person confirmed. Unconfirmed candidates count for nothing.",
"temporal_proximity": "Created close together in time.",
"shared_metadata": "Matching metadata you supplied.",
"user_confirmation": "A person confirmed it. This dominates everything else.",
"user_rejection": "A person rejected it. This drives the score to zero.",
}
rows := make([]publicWeightRow, 0, len(relationships.Weights))
for _, sig := range relationships.WeightOrder() {
rows = append(rows, publicWeightRow{
Signal: string(sig),
Weight: relationships.Weights[sig],
Means: explain[string(sig)],
})
}
return rows
}