-
Notifications
You must be signed in to change notification settings - Fork 141
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Toby Padilla
committed
Jul 28, 2021
0 parents
commit d069006
Showing
8 changed files
with
611 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
smoothie | ||
.ssh |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
) |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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{} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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")) |