Skip to content

Commit

Permalink
v0.0.11: lcl command improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
benburkert committed Mar 1, 2024
1 parent c367e53 commit f7bcc7e
Show file tree
Hide file tree
Showing 25 changed files with 1,077 additions and 360 deletions.
66 changes: 66 additions & 0 deletions auth/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package auth

import (
"context"
"errors"

"github.com/anchordotdev/cli"
"github.com/anchordotdev/cli/api"
"github.com/anchordotdev/cli/auth/models"
"github.com/anchordotdev/cli/ui"
tea "github.com/charmbracelet/bubbletea"
)

type Client struct {
Config *cli.Config

Anc *api.Session
Hint tea.Model
Source string
}

func (c Client) Perform(ctx context.Context, drv *ui.Driver) (*api.Session, error) {
var newClientErr, userInfoErr error

drv.Activate(ctx, &models.Client{})

if c.Anc == nil {
c.Anc, newClientErr = api.NewClient(c.Config)
if newClientErr != nil && !errors.Is(newClientErr, api.ErrSignedOut) {
return nil, newClientErr
}
}

drv.Send(models.ClientProbed(true))

if newClientErr == nil {
_, userInfoErr := c.Anc.UserInfo(ctx)
if userInfoErr != nil && !errors.Is(userInfoErr, api.ErrSignedOut) {
return nil, userInfoErr
}
}

drv.Send(models.ClientTested(true))

if errors.Is(newClientErr, api.ErrSignedOut) || errors.Is(userInfoErr, api.ErrSignedOut) {
if c.Hint == nil {
c.Hint = &models.SignInHint{}
}
cmd := &SignIn{
Config: c.Config,
Hint: c.Hint,
Source: c.Source,
}
err := cmd.RunTUI(ctx, drv)
if err != nil {
return nil, err
}

c.Anc, err = api.NewClient(c.Config)
if err != nil {
return nil, err
}
}

return c.Anc, nil
}
59 changes: 59 additions & 0 deletions auth/models/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package models

import (
"fmt"
"strings"

"github.com/anchordotdev/cli/ui"
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
)

type AuditAuthenticationWhoami string

type ClientProbed bool
type ClientTested bool

type Client struct {
spinner spinner.Model

probed bool
tested bool
}

func (m *Client) Init() tea.Cmd {
m.spinner = ui.WaitingSpinner()

return m.spinner.Tick
}

func (m *Client) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case ClientProbed:
m.probed = bool(msg)
return m, nil
case ClientTested:
m.tested = bool(msg)
return m, nil
default:
var cmd tea.Cmd
m.spinner, cmd = m.spinner.Update(msg)
return m, cmd
}
}

func (m *Client) View() string {
var b strings.Builder

if !m.probed {
fmt.Fprintln(&b, ui.StepInProgress(fmt.Sprintf("Checking authentication: probing credentials locally…%s", m.spinner.View())))
return b.String()
}

if !m.tested {
fmt.Fprintln(&b, ui.StepInProgress(fmt.Sprintf("Checking authentication: testing credentials remotely…%s", m.spinner.View())))
return b.String()
}

return b.String()
}
26 changes: 16 additions & 10 deletions auth/models/signin.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,27 @@ import (
tea "github.com/charmbracelet/bubbletea"
)

type SignInPreamble struct {
Message string
}
type SignInHeader struct{}

func (SignInPreamble) Init() tea.Cmd { return nil }
func (SignInHeader) Init() tea.Cmd { return nil }

func (m SignInPreamble) Update(tea.Msg) (tea.Model, tea.Cmd) { return m, nil }
func (m *SignInHeader) Update(tea.Msg) (tea.Model, tea.Cmd) { return m, nil }

func (m SignInPreamble) View() string {
func (m *SignInHeader) View() string {
var b strings.Builder
fmt.Fprintln(&b, ui.Header(fmt.Sprintf("Signin to Anchor.dev %s", ui.Whisper("`anchor auth signin`"))))
if m.Message != "" {
fmt.Fprintln(&b, m.Message)
}
return b.String()
}

type SignInHint struct{}

func (SignInHint) Init() tea.Cmd { return nil }

func (m SignInHint) Update(tea.Msg) (tea.Model, tea.Cmd) { return m, nil }

func (m SignInHint) View() string {
var b strings.Builder
fmt.Fprintln(&b, ui.StepHint("Please sign up or sign in with your Anchor account."))
return b.String()
}

Expand Down Expand Up @@ -64,7 +70,7 @@ func (m *SignInPrompt) View() string {
return b.String()
}

fmt.Fprintln(&b, ui.StepDone(fmt.Sprintf("Copied your user code to your clipboard.")))
fmt.Fprintln(&b, ui.StepDone(fmt.Sprintf("Copied your user code %s to your clipboard.", ui.Emphasize(m.UserCode))))
fmt.Fprintln(&b, ui.StepDone(fmt.Sprintf("Opened %s in your browser", ui.URL(m.VerificationURL))))

return b.String()
Expand Down
15 changes: 10 additions & 5 deletions auth/signin.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/atotto/clipboard"
tea "github.com/charmbracelet/bubbletea"
"github.com/cli/browser"
"github.com/mattn/go-isatty"
"github.com/muesli/termenv"
Expand All @@ -27,8 +28,9 @@ var (
type SignIn struct {
Config *cli.Config

Preamble string
Source string
Source string

Hint tea.Model
}

func (s SignIn) UI() cli.UI {
Expand Down Expand Up @@ -135,9 +137,12 @@ func (s *SignIn) runTTY(ctx context.Context, tty termenv.File) error {
}

func (s *SignIn) RunTUI(ctx context.Context, drv *ui.Driver) error {
drv.Activate(ctx, models.SignInPreamble{
Message: s.Preamble,
})
drv.Activate(ctx, &models.SignInHeader{})

if s.Hint == nil {
s.Hint = &models.SignInHint{}
}
drv.Activate(ctx, s.Hint)

anc, err := api.NewClient(s.Config)
if err != nil && err != api.ErrSignedOut {
Expand Down
7 changes: 4 additions & 3 deletions cert/models/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ func (m *Provision) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

func (m *Provision) View() string {
var b strings.Builder
fmt.Fprintln(&b, ui.Header("Provision Certificate"))
fmt.Fprintln(&b, ui.StepHint("You can manually use these certificate files or automate your certificates by following our setup guide."))
fmt.Fprintln(&b, ui.Header(fmt.Sprintf("Provision Certificate %s", ui.Whisper("`anchor lcl mkcert`"))))

if m.certFile == "" {
fmt.Fprintln(&b, ui.StepInProgress(fmt.Sprintf("Provisioning certificate for [%s]… %s",
Expand All @@ -55,7 +54,9 @@ func (m *Provision) View() string {
fmt.Fprintln(&b, ui.StepDone(fmt.Sprintf("Provisioned certificate for [%s].", ui.Domains(m.Domains))))
fmt.Fprintln(&b, ui.StepDone(fmt.Sprintf("Wrote certificate to %s", ui.Emphasize(m.certFile))))
fmt.Fprintln(&b, ui.StepDone(fmt.Sprintf("Wrote chain to %s", ui.Emphasize(m.chainFile))))
fmt.Fprintln(&b, ui.StepDone(fmt.Sprintf("Wrote key to %s", ui.Emphasize(m.chainFile))))
fmt.Fprintln(&b, ui.StepDone(fmt.Sprintf("Wrote key to %s", ui.Emphasize(m.keyFile))))
fmt.Fprintln(&b, ui.StepHint("You can use these certificate files to manually configure your application and"))
fmt.Fprintln(&b, ui.StepHint("start using HTTPS in your development environment."))

return b.String()
}
12 changes: 9 additions & 3 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,21 @@ type Config struct {
Subdomain string `desc:"Subdomain for lcl.host diagnostic service." flag:"subdomain" env:"SUBDOMAIN" json:"subdomain" toml:"subdomain"`

DiagnosticAddr string `default:":4433" desc:"Local server address" flag:"addr,a" env:"ADDR" json:"address" toml:"address"`
LclHostURL string `default:"https://lcl.anchor.systems" env:"LCL_HOST_URL"`
LclHostURL string `default:"https://lcl.host" env:"LCL_HOST_URL"`

Detect struct {
Audit struct {
} `cmd:"audit"`

Config struct {
} `cmd:"config"`

Setup struct {
PackageManager string `desc:"Package manager to use for integrating Anchor." flag:"package-manager" env:"PACKAGE_MANAGER" json:"package_manager" toml:"package-manager"`
Service string `desc:"Name for lcl.host service." flag:"service" env:"SERVICE" json:"service" toml:"service"`
Subdomain string `desc:"Subdomain for lcl.host service." flag:"subdomain" env:"SUBDOMAIN" json:"subdomain" toml:"subdomain"`
File string `desc:"File Anchor should use to detect package manager." flag:"file" env:"PACKAGE_MANAGER_FILE" json:"file" toml:"file"`
Language string `desc:"Language to use for integrating Anchor." flag:"language" json:"language" toml:"language"`
} `cmd:"detect"`
} `cmd:"setup"`
} `cmd:"lcl"`

Trust struct {
Expand Down
22 changes: 18 additions & 4 deletions cmd/anchor/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,25 @@ var (

SubCommands: []*cli.Command{
{
UI: lcl.Detect{Config: cfg}.UI(),
UI: lcl.Audit{Config: cfg}.UI(),

Name: "detect",
Use: "detect",
Short: "Detect Framework/Language",
Name: "audit",
Use: "audit",
Short: "Audit lcl.host HTTPS Local Development Environment",
},
{
UI: lcl.LclConfig{Config: cfg}.UI(),

Name: "config",
Use: "config",
Short: "Configure System for lcl.host Local Development",
},
{
UI: lcl.Setup{Config: cfg}.UI(),

Name: "setup",
Use: "setup",
Short: "Setup lcl.host Application",
},
},
},
Expand Down
104 changes: 104 additions & 0 deletions lcl/audit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package lcl

import (
"context"

"github.com/anchordotdev/cli"
"github.com/anchordotdev/cli/api"
"github.com/anchordotdev/cli/auth"
"github.com/anchordotdev/cli/lcl/models"
"github.com/anchordotdev/cli/trust"
"github.com/anchordotdev/cli/ui"
)

type Audit struct {
Config *cli.Config

anc *api.Session
orgSlug, realmSlug string
}

func (c Audit) UI() cli.UI {
return cli.UI{
RunTUI: c.run,
}
}

func (c Audit) run(ctx context.Context, drv *ui.Driver) error {
var err error
cmd := &auth.Client{
Config: c.Config,
Anc: c.anc,
Source: "lclhost",
}
c.anc, err = cmd.Perform(ctx, drv)
if err != nil {
return err
}

drv.Activate(ctx, &models.AuditHeader{})
drv.Activate(ctx, &models.AuditHint{})

_, err = c.perform(ctx, drv)
if err != nil {
return err
}

return nil
}

type LclAuditResult struct {
diagnosticServiceExists, trusted bool
}

func (c Audit) perform(ctx context.Context, drv *ui.Driver) (*LclAuditResult, error) {
var result = LclAuditResult{}

drv.Activate(ctx, &models.AuditResources{})

if c.orgSlug == "" {
userInfo, err := c.anc.UserInfo(ctx)
if err != nil {
return nil, err
}
c.orgSlug = userInfo.PersonalOrg.Slug
}

if c.realmSlug == "" {
c.realmSlug = "localhost"
}

var diagnosticService *api.Service
services, err := c.anc.GetOrgServices(ctx, c.orgSlug)
if err != nil {
return nil, err
}
for _, service := range services {
if service.ServerType == "diagnostic" {
diagnosticService = &service
}
}

if diagnosticService == nil {
drv.Send(models.AuditResourcesNotFoundMsg{})
} else {
result.diagnosticServiceExists = true
drv.Send(models.AuditResourcesFoundMsg{})
}

drv.Activate(ctx, &models.AuditTrust{})

// FIXME: use config anc?
trustStoreAuditResult, err := trust.PerformAudit(ctx, c.Config, c.anc, c.orgSlug, c.realmSlug)
if err != nil {
return nil, err
}

missingCount := len(trustStoreAuditResult.Missing)
result.trusted = missingCount == 0
drv.Send(models.AuditTrustMissingMsg(missingCount))

// TODO: audit local app status

return &result, nil
}
Loading

0 comments on commit f7bcc7e

Please sign in to comment.