Browse Source

feat: implemented all features besides wireguard

master
jb 3 weeks ago
parent
commit
085cda7251
11 changed files with 539 additions and 73 deletions
  1. +1
    -0
      .gitignore
  2. +0
    -27
      internal/docker/docker.go
  3. +57
    -6
      internal/tui/cmds.go
  4. +75
    -0
      internal/tui/config.go
  5. +96
    -0
      internal/tui/docker.go
  6. +10
    -9
      internal/tui/form.go
  7. +70
    -11
      internal/tui/model.go
  8. +9
    -0
      internal/tui/steps.go
  9. +3
    -0
      internal/tui/styles.go
  10. +97
    -14
      internal/tui/update.go
  11. +121
    -6
      internal/tui/view.go

+ 1
- 0
.gitignore View File

@ -0,0 +1 @@
config.toml

+ 0
- 27
internal/docker/docker.go View File

@ -1,27 +0,0 @@
package tui
import (
"fmt"
"os/exec"
)
func RunContainer(image string, name string, port int) error {
cmd := exec.Command(
"docker", "run",
"-d",
"--name", name,
"-p", fmt.Sprintf("%d:%d", port, port),
image,
)
return cmd.Run()
}
func PullImage(image string) (string, error) {
cmd := exec.Command("docker", "pull", image)
out, err := cmd.CombinedOutput()
return string(out), err
}

+ 57
- 6
internal/tui/cmds.go View File

@ -1,6 +1,7 @@
package tui
import (
"fmt"
"os/exec"
"time"
@ -14,11 +15,18 @@ type DockerCheckedMsg struct{ Installed bool }
type DockerInstalledMsg struct{ Err error }
type TickMsg struct{}
// Image downloading
// Image downloading and checking
type ImageCheckMsg struct {
Exists bool
Err error
}
type ImageDownloadFinishedMsg struct {
Err error
Message string
Err error
}
type DownloadTickMsg struct{}
type ConfigFileMsg struct{ Err error }
type DockerRunMsg struct{ Err error }
// --- Commands ---
@ -29,18 +37,61 @@ func CheckDockerCmd() tea.Cmd {
}
}
func tickCmd() tea.Cmd {
func TickCmd() tea.Cmd {
return tea.Tick(100*time.Millisecond, func(t time.Time) tea.Msg {
return TickMsg{}
})
}
func downloadImageCmd() tea.Cmd {
func DownloadImageCmd(username, password string) tea.Cmd {
return func() tea.Msg {
url := imageName
loginOut, err := exec.Command(
"docker", "login", url,
"-u", username,
"-p", password,
).CombinedOutput()
if err != nil {
return ImageDownloadFinishedMsg{
Message: string(loginOut),
Err: fmt.Errorf("falha no login: %w", err),
}
}
err := docker.PullImage("hub.davinti.com.br:443/app-dono/app-cliente:latest")
message, err := PullImage(url)
return ImageDownloadFinishedMsg{Message: message, Err: err}
}
}
func CheckImageCmd(image string) tea.Cmd {
return func() tea.Msg {
cmd := exec.Command("docker", "image", "inspect", image)
err := cmd.Run()
return ImageCheckMsg{
Exists: err == nil,
Err: err,
}
}
}
func GenerateConfigFile(cv ConfigValues, path string) tea.Cmd {
return func() tea.Msg {
err := WriteConfigFile(cv, path)
return ConfigFileMsg{
Err: err,
}
}
}
func RunAppContainer(image, name, filePath, destinationPath string, cv ConfigValues) tea.Cmd {
return func() tea.Msg {
err := RunAppClienteContainer(image, name, filePath, destinationPath, cv)
return ImageDownloadFinishedMsg{
return DockerRunMsg{
Err: err,
}
}


+ 75
- 0
internal/tui/config.go View File

@ -0,0 +1,75 @@
package tui
import (
"fmt"
"os"
"strconv"
"strings"
)
func GenerateConfigTOML(cv ConfigValues) (string, error) {
var sb strings.Builder
// [server]
sb.WriteString("# Server Configuration\n")
sb.WriteString("[server]\n")
sb.WriteString(fmt.Sprintf("port = %s\n", cv.Server["port"]))
sb.WriteString(fmt.Sprintf("timeout_seconds = %s\n", cv.Server["timeout"]))
sb.WriteString(fmt.Sprintf("environment = %q\n", cv.Server["environment"]))
sb.WriteString("\n")
// [database]
sb.WriteString("# Database Configuration\n")
sb.WriteString("[database]\n")
sb.WriteString(fmt.Sprintf("type = %q\n", cv.Database["database_type"]))
sb.WriteString(fmt.Sprintf("url = %q\n", cv.Database["database_url"]))
sb.WriteString(fmt.Sprintf("max_conns = %s\n", cv.Database["max_conns"]))
sb.WriteString(fmt.Sprintf("min_conns = %s\n", cv.Database["min_conns"]))
sb.WriteString("\n")
// [certificate]
sb.WriteString("# Certificate Options\n")
sb.WriteString("[certificate]\n")
sb.WriteString(fmt.Sprintf("cert_path = %q\n", "/app/certs/"+cv.Cert["cert_name"]))
sb.WriteString(fmt.Sprintf("key_path = %q\n", "/app/certs/"+cv.Cert["key_name"]))
sb.WriteString(fmt.Sprintf("ca_path = %q\n", "/app/certs/"+cv.Cert["ca_name"]))
sb.WriteString(fmt.Sprintf("server_name = %q\n", cv.Cert["server_name"]))
sb.WriteString("\n")
// [application] — hardcoded / pre-defined
sb.WriteString("# Pre-defined options\n")
sb.WriteString("[application]\n")
sb.WriteString("erp = \"TOTVS\"\n")
sb.WriteString("central_server_url = \"https://warden:8080\"\n")
sb.WriteString("api_key = \"super secreto\"\n")
sb.WriteString("\n")
// [log] — hardcoded defaults
sb.WriteString("[log]\n")
sb.WriteString("level = \"debug\"\n")
sb.WriteString("format = \"text\" # Options: \"json\" or \"text\"\n")
return sb.String(), nil
}
func WriteConfigFile(cv ConfigValues, path string) error {
// Validate numeric fields before writing
numericFields := map[string]string{
"port": cv.Server["port"],
"timeout": cv.Server["timeout"],
"max_conns": cv.Database["max_conns"],
"min_conns": cv.Database["min_conns"],
}
for field, val := range numericFields {
if _, err := strconv.Atoi(val); err != nil {
return fmt.Errorf("campo %q tem valor inválido: %q", field, val)
}
}
content, err := GenerateConfigTOML(cv)
if err != nil {
return err
}
return os.WriteFile(path, []byte(content), 0644)
}

+ 96
- 0
internal/tui/docker.go View File

@ -0,0 +1,96 @@
package tui
import (
"fmt"
"os/exec"
"path/filepath"
"strings"
"time"
)
func RunContainer(image string, name string, port int) error {
cmd := exec.Command(
"docker", "run",
"-d",
"--name", name,
"-p", fmt.Sprintf("%d:%d", port, port),
image,
)
return cmd.Run()
}
func PullImage(image string) (string, error) {
cmd := exec.Command("docker", "pull", image)
out, err := cmd.CombinedOutput()
return string(out), err
}
func ImageExists(image string) bool {
cmd := exec.Command("docker", "image", "inspect", image)
err := cmd.Run()
return err == nil
}
func PushFileToContainer(container, filePath, destinationPath string) bool {
dest := fmt.Sprintf("%s:%s", container, destinationPath)
cmd := exec.Command("docker", "cp", filePath, dest)
err := cmd.Run()
return err == nil
}
func RunAppClienteContainer(image, containerName, configPath, configDestinationPath string, cv ConfigValues) error {
removeExistingContainer(containerName)
absPath, err := filepath.Abs(configPath)
if err != nil {
return fmt.Errorf("erro ao resolver caminho absoluto: %w", err)
}
cmd := exec.Command(
"docker", "run", "-d",
"--name", containerName,
"--network", "app-dono_app",
"--restart", "unless-stopped",
"-v", fmt.Sprintf("%s:%s", absPath, configDestinationPath), // Config mapping
"-v", fmt.Sprintf("%s:/app/certs", cv.Cert["cert_dir_path"]), // Certs mapping
image,
)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("docker run falhou: %w\noutput: %s", err, string(out))
}
time.Sleep(2 * time.Second)
containerID := strings.TrimSpace(string(out))
return verifyContainerRunning(containerID)
}
func removeExistingContainer(name string) {
exec.Command("docker", "stop", name).Run()
exec.Command("docker", "rm", name).Run()
}
func verifyContainerRunning(containerID string) error {
cmd := exec.Command("docker", "inspect", "--format", "{{.State.Status}}", containerID)
out, err := cmd.Output()
if err != nil {
return fmt.Errorf("erro ao inspecionar container: %w", err)
}
status := strings.TrimSpace(string(out))
if status != "running" {
logCmd := exec.Command("docker", "logs", "--tail", "20", containerID)
logs, _ := logCmd.CombinedOutput()
return fmt.Errorf("container parou com status %q.\nlogs:\n%s", status, string(logs))
}
return nil
}

+ 10
- 9
internal/tui/form.go View File

@ -18,12 +18,13 @@ const (
)
type FormField struct {
Id string
Label string
Placeholder string
Default string
Type FieldType
Options []string // for FieldTypeSelect
optionIdx int // for FieldTypeSelect
OptionIdx int // for FieldTypeSelect
CharLimit int
input textinput.Model
}
@ -41,7 +42,7 @@ func NewFormStep(title string, fields []FormField) FormStep {
for j, opt := range f.Fields[i].Options {
// Apply default option
if opt == f.Fields[i].Default {
f.Fields[i].optionIdx = j
f.Fields[i].OptionIdx = j
break
}
}
@ -81,13 +82,13 @@ func (f *FormStep) Update(msg tea.Msg) (done bool, cmd tea.Cmd) {
switch msg.String() {
// Select specific inputs
case "left", "h":
if isSelect && focused.optionIdx > 0 {
focused.optionIdx--
if isSelect && focused.OptionIdx > 0 {
focused.OptionIdx--
}
case "right", "l":
if isSelect && focused.optionIdx < len(focused.Options)-1 {
focused.optionIdx++
if isSelect && focused.OptionIdx < len(focused.Options)-1 {
focused.OptionIdx++
}
// Generic inputs
@ -120,9 +121,9 @@ func (f *FormStep) Values() map[string]string {
out := make(map[string]string)
for _, field := range f.Fields {
if field.Type == FieldTypeSelect {
out[field.Label] = field.Options[field.optionIdx]
out[field.Id] = field.Options[field.OptionIdx]
} else {
out[field.Label] = field.input.Value()
out[field.Id] = field.input.Value()
}
}
return out
@ -155,7 +156,7 @@ func renderField(field FormField, focused bool) string {
case FieldTypeSelect:
var opts []string
for i, opt := range field.Options {
if i == field.optionIdx {
if i == field.OptionIdx {
opts = append(opts, SelectedStyle.Render("[ "+opt+" ]"))
} else {
opts = append(opts, DimStyle.Render(" "+opt+" "))


+ 70
- 11
internal/tui/model.go View File

@ -1,37 +1,78 @@
package tui
import (
"charm.land/bubbles/v2/spinner"
tea "charm.land/bubbletea/v2"
)
type Model struct {
currentStep step
cursor int
width int
height int
spinner spinner.Model
loading bool
dockerInstalled bool
checkDockerDone bool
checkProgress float64
isPublicIP bool
downloadDone bool
downloadMessage string
downloadError error
loginData DockerLoginData
isPublicIP bool
loginForm FormStep
serverForm FormStep
dbForm FormStep
certForm FormStep
configValues ConfigValues
finishedFile bool
configFileError error
finishedDockerRun bool
dockerRunError error
err error
}
type DockerLoginData struct {
Login string
Password string
}
type ConfigValues struct {
Login map[string]string
Server map[string]string
Database map[string]string
Cert map[string]string
}
func InitialModel() Model {
s := spinner.New()
s.Spinner = spinner.Dot
s.Style = SpinnerStyle
return Model{
currentStep: StepCheckDocker,
loginForm: NewFormStep("Login no Repositório Docker", []FormField{
{
Id: "user",
Label: "Usuário",
Placeholder: "usuario",
Type: FieldTypeText,
},
{
Id: "password",
Label: "Senha",
Placeholder: "senhaForte123",
Type: FieldTypePassword,
},
}),
serverForm: NewFormStep("Servidor", []FormField{
{
Id: "port",
Label: "Porta",
Placeholder: "8081",
Default: "8081",
@ -39,6 +80,7 @@ func InitialModel() Model {
CharLimit: 4,
},
{
Id: "timeout",
Label: "Timeout (Segundos)",
Placeholder: "30",
Default: "30",
@ -46,6 +88,7 @@ func InitialModel() Model {
CharLimit: 3,
},
{
Id: "environment",
Label: "Ambiente",
Default: "production",
Type: FieldTypeSelect,
@ -54,24 +97,28 @@ func InitialModel() Model {
}),
dbForm: NewFormStep("Banco de Dados", []FormField{
{
Id: "database_type",
Label: "Tipo do Banco",
Default: "postgres",
Type: FieldTypeSelect,
Options: []string{"postgres", "oracle"},
},
{
Id: "database_url",
Label: "URL de acesso",
Placeholder: "postgres://usuario:senha@banco:5432/app_dono_db",
Default: "postgres://usuario:senha@banco:5432/app_dono_db",
Type: FieldTypeText,
},
{
Id: "max_conns",
Label: "Conexões ativas (máximo)",
Placeholder: "10",
Default: "10",
Type: FieldTypeNumber,
},
{
Id: "min_conns",
Label: "Conexões ativas (mínimo)",
Placeholder: "2",
Default: "2",
@ -80,33 +127,45 @@ func InitialModel() Model {
}),
certForm: NewFormStep("Certificado", []FormField{
{
Label: "Caminho para o arquivo do certificado",
Placeholder: "/caminho/para/certificado.crt",
Default: "/caminho/para/certificado.crt",
Id: "cert_dir_path",
Label: "Caminho para o diretório dos certificados (será montado no container)",
Placeholder: "/caminho/para/diretorio",
Default: "/caminho/para/diretorio",
Type: FieldTypeText,
},
{
Id: "cert_name",
Label: "Nome do arquivo do certificado",
Placeholder: "certificado.crt",
Default: "certificado.crt",
Type: FieldTypeText,
},
{
Label: "Caminho para o arquivo da chave",
Placeholder: "/caminho/para/chave.key",
Default: "/caminho/para/chave.key",
Id: "key_name",
Label: "Nome do arquivo da chave",
Placeholder: "chave.key",
Default: "chave.key",
Type: FieldTypeText,
},
{
Label: "Caminho para o arquivo da autoridade certificadora",
Placeholder: "/caminho/para/chaveCA.crt",
Default: "/caminho/para/chaveCA.crt",
Id: "ca_name",
Label: "Nome do arquivo da autoridade certificadora",
Placeholder: "chaveCA.crt",
Default: "chaveCA.crt",
Type: FieldTypeText,
},
{
Id: "server_name",
Label: "Nome do servidor",
Placeholder: "client",
Default: "client",
Type: FieldTypeText,
},
}),
spinner: s,
}
}
func (m Model) Init() tea.Cmd {
return tea.Batch(CheckDockerCmd(), tickCmd())
return tea.Batch(CheckDockerCmd(), TickCmd(), m.spinner.Tick)
}

+ 9
- 0
internal/tui/steps.go View File

@ -3,16 +3,25 @@ package tui
type step int
const (
// Docker Validation
StepCheckDocker step = iota
StepDockerInstall
// Image Fetching
StepDockerLogin
StepDownloadImage
// IP Stuff
StepIPQuestion
StepInstallWireguard
// Docker Config
StepServerConfig
StepDatabaseConfig
StepCertConfig
// Finalizing
StepGenerateFile
StepRunDocker
StepDone
)

+ 3
- 0
internal/tui/styles.go View File

@ -53,4 +53,7 @@ var (
Foreground(lipgloss.Color(secondary)).
Bold(true).
Border(lipgloss.NormalBorder(), false, false, true)
SpinnerStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("205"))
)

+ 97
- 14
internal/tui/update.go View File

@ -1,11 +1,18 @@
package tui
import (
"fmt"
"math/rand"
"charm.land/bubbles/v2/spinner"
tea "charm.land/bubbletea/v2"
)
const (
imageName = "hub.davinti.com.br:443/app-dono/app-cliente:latest"
configPath = "config.toml"
)
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if key, ok := msg.(tea.KeyPressMsg); ok {
switch key.String() {
@ -14,11 +21,32 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
case spinner.TickMsg:
var cmd tea.Cmd
m.spinner, cmd = m.spinner.Update(msg)
return m, cmd
}
switch m.currentStep {
case StepCheckDocker:
return m.updateCheckDocker(msg)
case StepDockerInstall:
return m.updateDockerInstall(msg)
case StepDockerLogin:
done, cmd := m.loginForm.Update(msg)
if done {
m.configValues.Login = m.loginForm.Values()
m.currentStep = StepDownloadImage
return m, DownloadImageCmd(m.configValues.Login["user"], m.configValues.Login["password"])
}
return m, cmd
case StepDownloadImage:
return m.updateDownloadImage(msg)
case StepIPQuestion:
@ -29,6 +57,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
done, cmd := m.serverForm.Update(msg)
if done {
m.configValues.Server = m.serverForm.Values()
m.currentStep = StepDatabaseConfig
}
@ -37,6 +66,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
done, cmd := m.dbForm.Update(msg)
if done {
m.configValues.Database = m.dbForm.Values()
m.currentStep = StepCertConfig
}
@ -45,10 +75,17 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
done, cmd := m.certForm.Update(msg)
if done {
m.currentStep = StepDone
m.configValues.Cert = m.certForm.Values()
m.currentStep = StepGenerateFile
return m, GenerateConfigFile(m.configValues, configPath)
}
return m, cmd
case StepGenerateFile:
return m.updateGenerateFile(msg)
case StepRunDocker:
return m.updateRunDocker(msg)
case StepDone:
return m, tea.Quit
}
@ -61,19 +98,19 @@ func (m Model) updateCheckDocker(msg tea.Msg) (tea.Model, tea.Cmd) {
case DockerCheckedMsg:
m.dockerInstalled = msg.Installed
m.checkDockerDone = true
case TickMsg:
if m.checkProgress < 1.0 {
m.checkProgress += 0.15 + (rand.Float64()*0.15 - 0.15)
m.checkProgress += 0.2 + (rand.Float64()*0.2 - 0.2)
if m.checkProgress > 1.0 {
m.checkProgress = 1.0
}
return m, tickCmd()
return m, TickCmd()
}
case tea.KeyPressMsg:
if m.checkDockerDone {
if m.checkDockerDone && m.checkProgress == 1 {
if m.dockerInstalled {
m.currentStep = StepIPQuestion
m.loading = true
m.currentStep = StepDockerLogin
} else {
m.currentStep = StepDockerInstall
}
@ -84,14 +121,27 @@ func (m Model) updateCheckDocker(msg tea.Msg) (tea.Model, tea.Cmd) {
}
func (m Model) updateDockerInstall(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
switch msg.(type) {
case tea.KeyPressMsg:
return m, tea.Quit
case DockerInstalledMsg:
if msg.Err != nil {
m.err = msg.Err
} else {
}
return m, nil
}
func (m Model) updateDownloadImage(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case ImageDownloadFinishedMsg:
m.downloadDone = true
m.downloadMessage = msg.Message
m.downloadError = msg.Err
return m, nil
case tea.KeyPressMsg:
if m.downloadDone && m.downloadError == nil {
m.currentStep = StepIPQuestion
} else if m.downloadDone {
return m, tea.Quit
}
}
@ -127,10 +177,43 @@ func (m Model) updateIPQuestion(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil
}
func (m Model) updateWaitForProgramQuit(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg.(type) {
func (m Model) updateGenerateFile(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case ConfigFileMsg:
m.finishedFile = true
m.configFileError = msg.Err
case tea.KeyPressMsg:
return m, tea.Quit
if m.finishedFile && m.configFileError != nil {
return m, tea.Quit
} else if m.finishedFile && m.configFileError == nil {
m.currentStep = StepRunDocker
return m, RunAppContainer(
imageName,
"app-dono-cliente",
configPath,
fmt.Sprintf("/app/%s", configPath),
m.configValues,
)
}
}
return m, nil
}
func (m Model) updateRunDocker(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case DockerRunMsg:
m.finishedDockerRun = true
m.dockerRunError = msg.Err
case tea.KeyPressMsg:
if m.finishedDockerRun && m.dockerRunError != nil {
return m, tea.Quit
} else if m.finishedDockerRun && m.dockerRunError == nil {
m.currentStep = StepDone
}
}
return m, nil


+ 121
- 6
internal/tui/view.go View File

@ -5,34 +5,63 @@ import (
"strings"
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
)
const (
header = "App do Dono - Instalador Cliente"
header = "App do Dono - Instalador Cliente"
defaultMsg = "ctrl+c: sair"
anyKeyOutMsg = "qualquer tecla: sair"
formMsg = "tab: próximo campo • enter: confirmar • ctrl+c: sair"
)
func (m Model) View() tea.View {
pad := strings.Repeat(" ", padding)
var body string
helpMsg := "ctrl+c: sair"
helpMsg := defaultMsg
switch m.currentStep {
// Docker stuff
case StepCheckDocker:
body = m.viewCheckDocker()
case StepDockerInstall:
body = m.viewDockerInstall()
helpMsg = "qualquer tecla: sair"
helpMsg = anyKeyOutMsg
case StepDockerLogin:
body = m.loginForm.View()
helpMsg = formMsg
case StepDownloadImage:
body = m.viewDownloadImage()
// IP Stuff
case StepIPQuestion:
body = m.viewIPQuestion()
// App Config Stuff
case StepServerConfig:
body = m.serverForm.View()
helpMsg = "tab: próximo campo • enter: confirmar • ctrl+c: sair"
helpMsg = formMsg
case StepDatabaseConfig:
body = m.dbForm.View()
helpMsg = "tab: próximo campo • enter: confirmar • ctrl+c: sair"
helpMsg = formMsg
case StepCertConfig:
body = m.certForm.View()
helpMsg = "tab: próximo campo • enter: confirmar • ctrl+c: sair"
helpMsg = formMsg
// Finalize
case StepGenerateFile:
body = m.viewGenerateFile()
if m.finishedFile && m.configFileError != nil {
helpMsg = anyKeyOutMsg
}
case StepRunDocker:
body = m.viewDockerRun()
if m.finishedDockerRun && m.dockerRunError != nil {
helpMsg = anyKeyOutMsg
}
case StepDone:
body = m.viewDoneMessage()
helpMsg = anyKeyOutMsg
}
help := HelpStyle.Render(helpMsg)
@ -88,6 +117,32 @@ func (m Model) viewDockerInstall() string {
return sb.String()
}
func (m Model) viewDownloadImage() string {
pad := strings.Repeat(" ", padding)
var sb strings.Builder
if !m.downloadDone {
sb.WriteString(pad + fmt.Sprintf("%s Baixando imagem do container...\n\n", m.spinner.View()))
return sb.String()
}
if m.downloadError == nil {
sb.WriteString(pad + "Instalação concluída com êxito.\n\n")
sb.WriteString(pad + "Pressione qualquer tecla para continuar.")
} else {
sb.WriteString(pad + "Instalação finalizada com erros.\n\n")
dualPad := pad + pad
errText := dualPad + m.downloadMessage + "\n" + dualPad + m.downloadError.Error()
sb.WriteString(ErrorStyle.Width(m.width - (padding * 2)).Align(lipgloss.Left).Render(errText))
sb.WriteString("\n\n" + pad + "Pressione qualquer tecla para sair.")
}
return sb.String()
}
func (m Model) viewIPQuestion() string {
pad := strings.Repeat(" ", padding)
@ -113,3 +168,63 @@ func (m Model) viewIPQuestion() string {
return sb.String()
}
func (m Model) viewGenerateFile() string {
pad := strings.Repeat(" ", padding)
var sb strings.Builder
if !m.finishedFile {
sb.WriteString(pad + m.spinner.View() + "Gerando arquivo. Aguarde...")
} else {
sb.WriteString(pad + "Geração do arquivo finalizada.\n\n")
if m.configFileError == nil {
sb.WriteString(pad + "Arquivo gerado com sucesso. Pressione qualquer tecla para seguir.")
} else {
sb.WriteString(pad + "Ocorreu um erro ao gerar o arquivo.\n\n")
dualPad := pad + pad
errText := dualPad + m.configFileError.Error()
sb.WriteString(ErrorStyle.Width(m.width - (padding * 2)).Align(lipgloss.Left).Render(errText))
sb.WriteString("\n\n" + pad + "Tente novamente.")
}
}
return sb.String()
}
func (m Model) viewDockerRun() string {
pad := strings.Repeat(" ", padding)
var sb strings.Builder
if !m.finishedDockerRun {
sb.WriteString(pad + m.spinner.View() + "Tentando iniciar o container Docker. Aguarde...")
} else {
sb.WriteString(pad + "Inicialização finalizada.\n\n")
if m.dockerRunError == nil {
sb.WriteString(pad + "Container Docker iniciado com sucesso. Pressione qualquer tecla para seguir.")
} else {
sb.WriteString(pad + "Ocorreu um erro ao iniciar o container.\n\n")
dualPad := pad + pad
errText := dualPad + m.dockerRunError.Error()
sb.WriteString(ErrorStyle.Width(m.width - (padding * 2)).Align(lipgloss.Left).Render(errText))
sb.WriteString("\n\n" + pad + "Tente novamente.")
}
}
return sb.String()
}
func (m Model) viewDoneMessage() string {
pad := strings.Repeat(" ", padding)
var sb strings.Builder
sb.WriteString(pad + "Instalação realizada com sucesso!")
return sb.String()
}

Loading…
Cancel
Save