Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
Toby Padilla committed Jul 28, 2021
0 parents commit d069006
Show file tree
Hide file tree
Showing 8 changed files with 611 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
smoothie
.ssh
14 changes: 14 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module smoothie

go 1.16

replace github.com/charmbracelet/charm => ../charm

require (
github.com/charmbracelet/bubbletea v0.14.0
github.com/charmbracelet/charm v0.8.6
github.com/charmbracelet/lipgloss v0.2.1
github.com/gliderlabs/ssh v0.3.3
github.com/meowgorithm/babyenv v1.3.0
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
)
346 changes: 346 additions & 0 deletions go.sum

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package main

import (
"smoothie/tui"

"github.com/meowgorithm/babyenv"
)

type Config struct {
KeyPath string `env:"SMOOTHIE_KEY_PATH" default:".ssh/smoothie_server_ed25519"`
Port int `env:"SMOOTHIE_PORT" default:"23231"`
}

func main() {
var cfg Config
err := babyenv.Parse(&cfg)
if err != nil {
panic(err)
}
s, err := NewServer(cfg.Port, cfg.KeyPath, tui.SessionHandler)
if err != nil {
panic(err)
}
err = s.Start()
if err != nil {
panic(err)
}
}
79 changes: 79 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package main

import (
"fmt"
"log"
"path/filepath"
"strings"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/charm/keygen"
"github.com/gliderlabs/ssh"
gossh "golang.org/x/crypto/ssh"
)

type SessionHandler func(ssh.Session) (tea.Model, error)

type Server struct {
server *ssh.Server
key gossh.PublicKey
handler SessionHandler
}

func NewServer(port int, keyPath string, handler SessionHandler) (*Server, error) {
s := &Server{
server: &ssh.Server{},
handler: handler,
}
s.server.Version = "OpenSSH_7.6p1"
s.server.Addr = fmt.Sprintf(":%d", port)
s.server.Handler = s.sessionHandler
s.server.PasswordHandler = s.passHandler
s.server.PublicKeyHandler = s.authHandler
kps := strings.Split(keyPath, string(filepath.Separator))
kp := strings.Join(kps[:len(kps)-1], string(filepath.Separator))
n := strings.TrimRight(kps[len(kps)-1], "_ed25519")
_, err := keygen.NewSSHKeyPair(kp, n, nil, "ed25519")
if err != nil {
return nil, err
}
k := ssh.HostKeyFile(keyPath)
err = s.server.SetOption(k)
if err != nil {
return nil, err
}
return s, nil
}

func (srv *Server) sessionHandler(s ssh.Session) {
hpk := s.PublicKey() != nil
log.Printf("%s connect %v %v\n", s.RemoteAddr().String(), hpk, s.Command())
m, err := srv.handler(s)
if err != nil {
log.Printf("%s error %v %s\n", s.RemoteAddr().String(), hpk, err)
s.Exit(1)
return
}
if m != nil {
p := tea.NewProgram(m, tea.WithInput(s), tea.WithOutput(s))
err = p.Start()
if err != nil {
log.Printf("%s error %v %s\n", s.RemoteAddr().String(), hpk, err)
s.Exit(1)
return
}
}
log.Printf("%s disconnect %v %v\n", s.RemoteAddr().String(), hpk, s.Command())
}

func (srv *Server) authHandler(ctx ssh.Context, key ssh.PublicKey) bool {
return true
}

func (srv *Server) passHandler(ctx ssh.Context, pass string) bool {
return true
}

func (srv *Server) Start() error {
return srv.server.ListenAndServe()
}
14 changes: 14 additions & 0 deletions tui/commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package tui

import (
tea "github.com/charmbracelet/bubbletea"
)

type windowMsg struct{}

func (m *Model) windowChangesCmd() tea.Msg {
w := <-m.windowChanges
m.width = w.Width
m.height = w.Height
return windowMsg{}
}
98 changes: 98 additions & 0 deletions tui/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package tui

import (
"fmt"

tea "github.com/charmbracelet/bubbletea"
"github.com/gliderlabs/ssh"
)

type sessionState int

const (
startState sessionState = iota
errorState
quittingState
quitState
)

type stateMsg struct{ state sessionState }
type infoMsg struct{ text string }
type errMsg struct{ err error }

func (e errMsg) Error() string {
return e.err.Error()
}

func SessionHandler(s ssh.Session) (tea.Model, error) {
pty, changes, active := s.Pty()
if !active {
return nil, fmt.Errorf("you need to do this from a terminal with PTY support")
}
return NewModel(pty.Window.Width, pty.Window.Height, changes), nil
}

type Model struct {
state sessionState
error string
info string
width int
height int
windowChanges <-chan ssh.Window
}

func NewModel(width int, height int, windowChanges <-chan ssh.Window) *Model {
m := &Model{
width: width,
height: height,
windowChanges: windowChanges,
}
m.state = startState
return m
}

func (m *Model) Init() tea.Cmd {
return tea.Batch(m.windowChangesCmd, tea.HideCursor)
}

func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds := make([]tea.Cmd, 0)
// Always allow state, error, info, window resize and quit messages
switch msg := msg.(type) {
case stateMsg:
m.state = msg.state
case tea.KeyMsg:
switch msg.String() {
case "q", "ctrl+c":
return m, tea.Quit
}
case errMsg:
m.error = msg.Error()
m.state = errorState
return m, nil
case infoMsg:
m.info = msg.text
case windowMsg:
cmds = append(cmds, m.windowChangesCmd)
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
}
return m, tea.Batch(cmds...)
}

func (m *Model) View() string {
pad := 6
h := headerStyle.Width(m.width - pad).Render("Smoothie")
f := footerStyle.Render(m.info)
s := ""
content := ""
switch m.state {
case errorState:
s += errorStyle.Render(fmt.Sprintf("Bummer: %s", m.error))
default:
s = normalStyle.Render(fmt.Sprintf("Doing something weird %d", m.state))
}
content = h + "\n" + s + "\n" + f
return appBoxStyle.Render(content)
}
30 changes: 30 additions & 0 deletions tui/style.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package tui

import (
"github.com/charmbracelet/lipgloss"
)

var appBoxStyle = lipgloss.NewStyle().
PaddingLeft(2).
PaddingRight(2).
MarginBottom(1)

var headerStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#670083")).
Align(lipgloss.Right).
Bold(true)

var normalStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF"))

var footerStyle = lipgloss.NewStyle().
BorderStyle(lipgloss.Border{Left: ">"}).
BorderForeground(lipgloss.Color("#6D6D6D")).
BorderLeft(true).
Foreground(lipgloss.Color("#373737")).
PaddingLeft(1).
MarginLeft(1).
Bold(true)

var errorStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#FF00000"))

0 comments on commit d069006

Please sign in to comment.