repos / pico

pico services - prose.sh, pastes.sh, imgs.sh, feeds.sh, pgs.sh
git clone https://github.com/picosh/pico.git

pico / pico
Eric Bower · 17 May 24

cli.go

  1package pico
  2
  3import (
  4	"fmt"
  5	"log/slog"
  6	"strings"
  7
  8	"github.com/charmbracelet/ssh"
  9	"github.com/charmbracelet/wish"
 10	"github.com/picosh/pico/db"
 11	"github.com/picosh/pico/shared"
 12	"github.com/picosh/pico/tui/common"
 13	"github.com/picosh/pico/tui/notifications"
 14	"github.com/picosh/pico/tui/plus"
 15)
 16
 17func getUser(s ssh.Session, dbpool db.DB) (*db.User, error) {
 18	var err error
 19	key, err := shared.KeyText(s)
 20	if err != nil {
 21		return nil, fmt.Errorf("key not found")
 22	}
 23
 24	user, err := dbpool.FindUserForKey(s.User(), key)
 25	if err != nil {
 26		return nil, err
 27	}
 28
 29	if user.Name == "" {
 30		return nil, fmt.Errorf("must have username set")
 31	}
 32
 33	return user, nil
 34}
 35
 36type Cmd struct {
 37	User    *db.User
 38	Session shared.CmdSession
 39	Log     *slog.Logger
 40	Dbpool  db.DB
 41	Write   bool
 42	Styles  common.Styles
 43}
 44
 45func (c *Cmd) output(out string) {
 46	_, _ = c.Session.Write([]byte(out + "\r\n"))
 47}
 48
 49func (c *Cmd) help() {
 50	helpStr := "Commands: [help, pico+]\n"
 51	c.output(helpStr)
 52}
 53
 54func (c *Cmd) plus() {
 55	view := plus.PlusView(c.User.Name, 80)
 56	c.output(view)
 57}
 58
 59func (c *Cmd) notifications() error {
 60	md := notifications.NotificationsView(c.Dbpool, c.User.ID, 80)
 61	c.output(md)
 62	return nil
 63}
 64
 65type CliHandler struct {
 66	DBPool db.DB
 67	Logger *slog.Logger
 68}
 69
 70func WishMiddleware(handler *CliHandler) wish.Middleware {
 71	dbpool := handler.DBPool
 72	log := handler.Logger
 73
 74	return func(next ssh.Handler) ssh.Handler {
 75		return func(sesh ssh.Session) {
 76			args := sesh.Command()
 77			if len(args) == 0 {
 78				next(sesh)
 79				return
 80			}
 81
 82			user, err := getUser(sesh, dbpool)
 83			if err != nil {
 84				wish.Errorf(sesh, "detected ssh command: %s\n", args)
 85				s := fmt.Errorf("error: you need to create an account before using the remote cli: %w", err)
 86				wish.Fatalln(sesh, s)
 87				return
 88			}
 89
 90			if len(args) > 0 && args[0] == "chat" {
 91				_, _, hasPty := sesh.Pty()
 92				if !hasPty {
 93					wish.Fatalln(
 94						sesh,
 95						"In order to render chat you need to enable PTY with the `ssh -t` flag",
 96					)
 97					return
 98				}
 99
100				pass, err := dbpool.UpsertToken(user.ID, "pico-chat")
101				if err != nil {
102					wish.Fatalln(sesh, err)
103					return
104				}
105				app, err := shared.NewSenpaiApp(sesh, user.Name, pass)
106				if err != nil {
107					wish.Fatalln(sesh, err)
108					return
109				}
110				app.Run()
111				app.Close()
112				return
113			}
114
115			opts := Cmd{
116				Session: sesh,
117				User:    user,
118				Log:     log,
119				Dbpool:  dbpool,
120				Write:   false,
121			}
122
123			cmd := strings.TrimSpace(args[0])
124			if len(args) == 1 {
125				if cmd == "help" {
126					opts.help()
127					return
128				} else if cmd == "pico+" {
129					opts.plus()
130					return
131				} else if cmd == "notifications" {
132					err := opts.notifications()
133					if err != nil {
134						wish.Fatalln(sesh, err)
135					}
136					return
137				} else {
138					next(sesh)
139					return
140				}
141			}
142
143			next(sesh)
144		}
145	}
146}