Skip to content

Commit 27efac0

Browse files
committed
Show current lookup progress in spinner
1 parent 5d46990 commit 27efac0

5 files changed

Lines changed: 901 additions & 23 deletions

File tree

internal/cli/cli.go

Lines changed: 245 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package cli
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"strings"
8+
"sync/atomic"
79
"time"
810

911
"UWP-TCP-Con/internal/ping"
@@ -14,22 +16,33 @@ type App struct {
1416
}
1517

1618
func NewApp() *App {
17-
return &App{inputTimeout: 3 * time.Second}
19+
return &App{
20+
inputTimeout: 3 * time.Second,
21+
}
1822
}
1923

2024
func (a *App) Run() error {
2125
for {
2226
config, err := a.collectConfig()
2327
if err != nil {
28+
if errors.Is(err, errAborted) {
29+
return nil
30+
}
2431
return err
2532
}
2633

2734
if err := a.execute(config); err != nil {
35+
if errors.Is(err, errAborted) {
36+
return nil
37+
}
2838
return err
2939
}
3040

3141
again, err := a.askAgain()
3242
if err != nil {
43+
if errors.Is(err, errAborted) {
44+
return nil
45+
}
3346
return err
3447
}
3548
if !again {
@@ -39,28 +52,117 @@ func (a *App) Run() error {
3952
}
4053

4154
type Config struct {
55+
Mode Mode
56+
Direct DirectConfig
57+
Lookup LookupConfig
58+
}
59+
60+
func (a *App) collectConfig() (Config, error) {
61+
mode, err := a.askMode()
62+
if err != nil {
63+
return Config{}, err
64+
}
65+
66+
if mode == ModeLookup {
67+
lookup, err := a.collectLookupConfig()
68+
if err != nil {
69+
return Config{}, err
70+
}
71+
return Config{Mode: mode, Lookup: lookup}, nil
72+
}
73+
74+
direct, err := a.collectDirectConfig()
75+
if err != nil {
76+
return Config{}, err
77+
}
78+
79+
return Config{Mode: mode, Direct: direct}, nil
80+
}
81+
82+
type Mode string
83+
84+
const (
85+
ModeDirect Mode = "direct"
86+
ModeLookup Mode = "lookup"
87+
)
88+
89+
type DirectConfig struct {
4290
Host string
4391
Port int
4492
Edition ping.Edition
4593
}
4694

47-
func (a *App) collectConfig() (Config, error) {
95+
type LookupConfig struct {
96+
Edition ping.Edition
97+
BaseHost string
98+
Port int
99+
Subdomains []string
100+
Endings []string
101+
}
102+
103+
func (a *App) collectDirectConfig() (DirectConfig, error) {
48104
edition, err := a.askEdition()
49105
if err != nil {
50-
return Config{}, err
106+
return DirectConfig{}, err
51107
}
52108

53109
host, err := a.askHost()
54110
if err != nil {
55-
return Config{}, err
111+
return DirectConfig{}, err
56112
}
57113

58114
port, err := a.askPort(edition)
59115
if err != nil {
60-
return Config{}, err
116+
return DirectConfig{}, err
117+
}
118+
119+
return DirectConfig{Host: host, Port: port, Edition: edition}, nil
120+
}
121+
122+
func (a *App) collectLookupConfig() (LookupConfig, error) {
123+
edition, err := a.askEdition()
124+
if err != nil {
125+
return LookupConfig{}, err
61126
}
62127

63-
return Config{Host: host, Port: port, Edition: edition}, nil
128+
subdomains, err := a.askSubdomainChoice()
129+
if err != nil {
130+
return LookupConfig{}, err
131+
}
132+
133+
baseHost, err := a.askBaseHost()
134+
if err != nil {
135+
return LookupConfig{}, err
136+
}
137+
138+
endings, err := a.askDomainEndings()
139+
if err != nil {
140+
return LookupConfig{}, err
141+
}
142+
143+
port, err := a.askPort(edition)
144+
if err != nil {
145+
return LookupConfig{}, err
146+
}
147+
148+
return LookupConfig{
149+
Edition: edition,
150+
BaseHost: baseHost,
151+
Port: port,
152+
Subdomains: subdomains,
153+
Endings: endings,
154+
}, nil
155+
}
156+
157+
func (a *App) askMode() (Mode, error) {
158+
index, err := selectOption("Startmodus", []string{"UWP/TCP Abfrage", "IP Lookup"})
159+
if err != nil {
160+
return "", err
161+
}
162+
if index == 1 {
163+
return ModeLookup, nil
164+
}
165+
return ModeDirect, nil
64166
}
65167

66168
func (a *App) askEdition() (ping.Edition, error) {
@@ -89,6 +191,69 @@ func (a *App) askHost() (string, error) {
89191
}
90192
}
91193

194+
func (a *App) askBaseHost() (string, error) {
195+
var errMsg string
196+
for {
197+
value, err := promptInput("IP/Domain ohne Endung", "z.B. example", errMsg)
198+
if err != nil {
199+
return "", err
200+
}
201+
value = strings.TrimSpace(value)
202+
if value == "" {
203+
errMsg = "Wert darf nicht leer sein"
204+
continue
205+
}
206+
return value, nil
207+
}
208+
}
209+
210+
func (a *App) askSubdomainChoice() ([]string, error) {
211+
index, err := selectOption("Subdomain", []string{"Eigene Subdomain", "Subdomain-Pool"})
212+
if err != nil {
213+
return nil, err
214+
}
215+
if index == 1 {
216+
return subdomainPool, nil
217+
}
218+
219+
var errMsg string
220+
for {
221+
value, err := promptInput("Subdomain (optional)", "z.B. play (leer lassen für keine)", errMsg)
222+
if err != nil {
223+
return nil, err
224+
}
225+
value = strings.TrimSpace(value)
226+
return []string{value}, nil
227+
}
228+
}
229+
230+
func (a *App) askDomainEndings() ([]string, error) {
231+
index, err := selectOption("Domain-Endung", []string{"Eigene Endung", "Endungs-Pool"})
232+
if err != nil {
233+
return nil, err
234+
}
235+
if index == 1 {
236+
endings, err := loadDomainEndings()
237+
if err != nil {
238+
return endings, nil
239+
}
240+
return endings, nil
241+
}
242+
var errMsg string
243+
for {
244+
value, err := promptInput("Domain-Endung", "z.B. com oder de", errMsg)
245+
if err != nil {
246+
return nil, err
247+
}
248+
value = normalizeEnding(value)
249+
if value == "" {
250+
errMsg = "Endung darf nicht leer sein"
251+
continue
252+
}
253+
return []string{value}, nil
254+
}
255+
}
256+
92257
func (a *App) askPort(edition ping.Edition) (int, error) {
93258
defaultPort := ping.DefaultPort(edition)
94259
var errMsg string
@@ -114,10 +279,21 @@ func (a *App) askPort(edition ping.Edition) (int, error) {
114279
}
115280

116281
func (a *App) execute(config Config) error {
282+
switch config.Mode {
283+
case ModeLookup:
284+
return a.executeLookup(config.Lookup)
285+
default:
286+
return a.executeDirect(config.Direct)
287+
}
288+
}
289+
290+
func (a *App) executeDirect(config DirectConfig) error {
117291
ctx, cancel := context.WithTimeout(context.Background(), a.inputTimeout)
118292
defer cancel()
119293

120-
resultText, err := withSpinner("Abfrage", "Server wird abgefragt", 120*time.Millisecond, func() (string, error) {
294+
resultText, err := withSpinner("Abfrage", func() string {
295+
return "Server wird abgefragt"
296+
}, 120*time.Millisecond, func() (string, error) {
121297
result, err := ping.Execute(ctx, config.Edition, config.Host, config.Port)
122298
if err != nil {
123299
return "", err
@@ -132,10 +308,72 @@ func (a *App) execute(config Config) error {
132308
return nil
133309
}
134310

311+
func (a *App) executeLookup(config LookupConfig) error {
312+
ctx := context.Background()
313+
314+
var current atomic.Value
315+
current.Store("Domains werden überprüft")
316+
formatProgress := func(subdomain, ending string) string {
317+
sub := subdomain
318+
if sub == "" {
319+
sub = "(keine)"
320+
}
321+
return fmt.Sprintf("Subdomain: %s | Endung: %s", sub, ending)
322+
}
323+
324+
resultText, err := withSpinner("IP Lookup", func() string {
325+
return current.Load().(string)
326+
}, 120*time.Millisecond, func() (string, error) {
327+
result, lookupErr := ping.LookupDomains(ctx, ping.LookupConfig{
328+
Edition: config.Edition,
329+
Port: config.Port,
330+
BaseHost: config.BaseHost,
331+
Subdomains: config.Subdomains,
332+
DomainEndings: config.Endings,
333+
Concurrency: 24,
334+
Progress: func(subdomain, ending string) {
335+
current.Store(formatProgress(subdomain, ending))
336+
},
337+
})
338+
if lookupErr != nil {
339+
return "", lookupErr
340+
}
341+
return formatLookupResult(result), nil
342+
})
343+
if err != nil {
344+
return err
345+
}
346+
347+
renderTextPage("Ergebnis", resultText)
348+
return nil
349+
}
350+
135351
func (a *App) askAgain() (bool, error) {
136352
index, err := selectOption("Nächster Schritt", []string{"Neue Abfrage", "Beenden"})
137353
if err != nil {
138354
return false, err
139355
}
140356
return index == 0, nil
141357
}
358+
359+
func formatLookupResult(result ping.LookupResult) string {
360+
var builder strings.Builder
361+
builder.WriteString(fmt.Sprintf("Kombinationen geprüft: %d/%d\n", result.Completed, result.Attempts))
362+
builder.WriteString(fmt.Sprintf("Treffer: %d\n", len(result.Matches)))
363+
builder.WriteString("\n")
364+
365+
if len(result.Matches) == 0 {
366+
builder.WriteString("Keine passenden Server gefunden.")
367+
return builder.String()
368+
}
369+
370+
for i, match := range result.Matches {
371+
if i > 0 {
372+
builder.WriteString("\n")
373+
}
374+
builder.WriteString(fmt.Sprintf("Host: %s\n", match.Host))
375+
builder.WriteString(match.Result.String())
376+
builder.WriteString("\n")
377+
}
378+
return builder.String()
379+
}

0 commit comments

Comments
 (0)