Skip to content
quarry Artifact [44460bf2d3]
Login

Artifact [44460bf2d3]

Artifact 44460bf2d356fe53caad4507b5b8572b54db4863765a0988d53acd6ce05f40b1:


package api

// chatidentity.go is which quarry account a bridged chat name is
// (oltp/chatidentity.go): fossil:stephan shown as stephan, because an
// administrator of the repository said so, or because it is the
// bridge's own login and the bridge signs in as it.

import (
	"context"
	"fmt"
	"log"

	"craftdesign.group/quarry/internal/oltp"
	"craftdesign.group/quarry/internal/ops"
)

// ChatIdentityStore is the chat_identity table, satisfied by *oltp.DB.
type ChatIdentityStore interface {
	SetChatIdentity(repo, remote, account, by string) error
	ChatIdentities(repo string) ([]oltp.ChatIdentity, error)
}

// bridgeLinkBy is the chat_identity.by of a bridge's own login: the
// forge knows it is the person who set the bridge up, because the
// bridge signs in as it with their password.
const bridgeLinkBy = "bridge"

// linkBridgeLogin links a bridge's own login to whoever set it up.
func (d Deps) linkBridgeLogin(repo, login, setter string) {
	if d.ChatIdentities == nil || login == "" || setter == "" {
		return
	}
	if err := d.ChatIdentities.SetChatIdentity(repo, "fossil:"+login, setter, bridgeLinkBy); err != nil {
		log.Printf("chat bridge %s: linking its login %s to %s: %v", repo, login, setter, err)
	}
}

type chatIdentitiesIn struct {
	Repo string `json:"repo" validate:"required,max=64"`
}

type chatIdentitiesOut struct {
	Repo  string              `json:"repo"`
	Links []oltp.ChatIdentity `json:"links"`
}

type chatIdentitySetIn struct {
	Repo string `json:"repo" validate:"required,max=64"`
	// Remote is the bridged name as the channel shows it, fossil:<login>.
	Remote string `json:"remote" validate:"required,max=140"`
	// Account is the quarry account it is; empty removes the link.
	Account string `json:"account" validate:"max=64"`
}

func registerChatIdentity(r *ops.Registry, d Deps) error {
	if err := ops.Register(r, ops.Op[chatIdentitiesIn, chatIdentitiesOut]{
		Name: "chat.identities",
		Caps: "C",
		Doc: "Which quarry accounts a repository channel's bridged names are: `fossil:<login>` shown as the account, " +
			"each link with who made it (an administrator, or `bridge` for the bridge's own login).",
		Kind:  ops.Query,
		Auth:  ops.Token,
		Route: "GET /api/v1/r/{repo}/chat/identities",
		Run: func(ctx context.Context, in chatIdentitiesIn) (chatIdentitiesOut, error) {
			if _, err := d.repoFor(ctx, in.Repo); err != nil {
				return chatIdentitiesOut{}, err
			}
			out := chatIdentitiesOut{Repo: in.Repo, Links: []oltp.ChatIdentity{}}
			if d.ChatIdentities == nil {
				return out, nil
			}
			links, err := d.ChatIdentities.ChatIdentities(in.Repo)
			if err != nil {
				return chatIdentitiesOut{}, err
			}
			if links != nil {
				out.Links = links
			}
			return out, nil
		},
	}); err != nil {
		return err
	}
	return ops.Register(r, ops.Op[chatIdentitySetIn, chatIdentitiesOut]{
		Name: "chat.identity.set",
		Caps: "a",
		Doc: "Say which quarry account a bridged chat name is, `fossil:<login>`, so the channel shows the account's name " +
			"and identicon for it; an empty account removes the link. An administrator's statement, recorded as theirs: " +
			"a fossil login proves nothing on its own.",
		Kind:  ops.Command,
		Auth:  ops.Token,
		Route: "POST /api/v1/r/{repo}/chat/identities",
		Run: func(ctx context.Context, in chatIdentitySetIn) (chatIdentitiesOut, error) {
			if _, err := d.repoFor(ctx, in.Repo); err != nil {
				return chatIdentitiesOut{}, err
			}
			if d.ChatIdentities == nil {
				return chatIdentitiesOut{}, fmt.Errorf("this forge keeps no chat identity links: %w", ops.ErrUnavailable)
			}
			if err := d.ChatIdentities.SetChatIdentity(in.Repo, in.Remote, in.Account, author(ctx)); err != nil {
				return chatIdentitiesOut{}, refusalAsInvalid(err)
			}
			links, err := d.ChatIdentities.ChatIdentities(in.Repo)
			if err != nil {
				return chatIdentitiesOut{}, err
			}
			if links == nil {
				links = []oltp.ChatIdentity{}
			}
			return chatIdentitiesOut{Repo: in.Repo, Links: links}, nil
		},
	})
}