Skip to content

Commit df06a39

Browse files
committed
Added progress bars to all actions
1 parent 4ca662f commit df06a39

6 files changed

Lines changed: 126 additions & 1 deletion

File tree

utils/delete.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func DeletePath(vaultPath string, session *Session) error {
2020
repoURL := fmt.Sprintf("git@github.com:%s/.zephyrus.git", session.Username)
2121

2222
// 1. Navigate to the target
23+
PrintProgressStep(1, 4, "Locating path in vault...")
2324
parts := strings.Split(strings.Trim(vaultPath, "/"), "/")
2425
currentMap := session.Index
2526
var targetName = parts[len(parts)-1]
@@ -37,8 +38,10 @@ func DeletePath(vaultPath string, session *Session) error {
3738
if !exists {
3839
return fmt.Errorf("path '%s' not found in vault", vaultPath)
3940
}
41+
PrintCompletionLine("Path located")
4042

4143
// 2. Identify all storage IDs to be removed
44+
PrintProgressStep(2, 4, "Preparing deletion...")
4245
var idsToDelete []string
4346
if targetEntry.Type == "file" {
4447
idsToDelete = append(idsToDelete, targetEntry.RealName)
@@ -57,8 +60,10 @@ func DeletePath(vaultPath string, session *Session) error {
5760
collectIDs(targetEntry)
5861
fmt.Printf("Preparing to recursively delete folder '%s' (%d files)...\n", vaultPath, len(idsToDelete))
5962
}
63+
PrintCompletionLine("Deletion prepared")
6064

6165
// 3. Git Setup
66+
PrintProgressStep(3, 4, "Updating vault index...")
6267
storer := memory.NewStorage()
6368
fs := memfs.New()
6469
publicKeys, _ := ssh.NewPublicKeys("git", session.RawKey, "")
@@ -95,6 +100,7 @@ func DeletePath(vaultPath string, session *Session) error {
95100
w.Add(".config/index")
96101

97102
// 7. Commit the changes
103+
PrintProgressStep(4, 4, "Uploading to GitHub...")
98104
// Ensure you are passing *git.CommitOptions, not just the Signature
99105
commit, err := w.Commit(session.Settings.CommitMessage, &git.CommitOptions{
100106
Author: &object.Signature{
@@ -114,6 +120,7 @@ func DeletePath(vaultPath string, session *Session) error {
114120
if err != nil {
115121
return err
116122
}
123+
PrintCompletionLine("Deletion completed")
117124

118125
return nil
119126
}

utils/download.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import (
77
"fmt"
88
"os"
99
"strings"
10+
"time"
1011
)
1112

1213
func DownloadFile(vaultPath string, outputPath string, session *Session) error {
1314
// 1. Use your custom FindEntry logic to navigate the nested maps
15+
PrintProgressStep(1, 5, "Locating file in vault...")
1416
entry, err := session.Index.FindEntry(vaultPath)
1517
if err != nil {
1618
return fmt.Errorf("could not find file in vault: %w", err)
@@ -20,16 +22,20 @@ func DownloadFile(vaultPath string, outputPath string, session *Session) error {
2022
if entry.Type == "folder" {
2123
return fmt.Errorf("'%s' is a directory, you can only download individual files", vaultPath)
2224
}
25+
PrintCompletionLine("File located: " + entry.RealName)
2326

2427
fmt.Printf("Downloading %s (Storage ID: %s)...\n", vaultPath, entry.RealName)
2528

2629
// 3. Fetch the encrypted hex-named file from GitHub
30+
PrintProgressStep(2, 5, "Fetching encrypted file from GitHub...")
2731
encryptedData, err := FetchRaw(session.Username, entry.RealName)
2832
if err != nil {
2933
return fmt.Errorf("failed to fetch storage file from remote: %w", err)
3034
}
35+
PrintCompletionLine("File fetched from GitHub")
3136

3237
// 4. Decrypt the file key from the index
38+
PrintProgressStep(3, 5, "Decrypting file key...")
3339
encryptedKey, err := hex.DecodeString(entry.FileKey)
3440
if err != nil {
3541
return fmt.Errorf("invalid file key in index: %w", err)
@@ -38,15 +44,25 @@ func DownloadFile(vaultPath string, outputPath string, session *Session) error {
3844
if err != nil {
3945
return fmt.Errorf("failed to decrypt file key: check your password")
4046
}
47+
PrintCompletionLine("File key decrypted")
4148

4249
// 5. Decrypt the file data with the file key
50+
PrintProgressStep(4, 5, "Decrypting file contents...")
51+
time.Sleep(time.Millisecond * 100) // Simulate work for visibility
4352
decryptedData, err := DecryptWithKey(encryptedData, fileKey)
4453
if err != nil {
4554
return fmt.Errorf("decryption failed: %w", err)
4655
}
56+
PrintCompletionLine("File contents decrypted")
4757

4858
// 6. Save to the local output path
49-
return os.WriteFile(outputPath, decryptedData, 0644)
59+
PrintProgressStep(5, 5, "Saving file to "+outputPath+"...")
60+
err = os.WriteFile(outputPath, decryptedData, 0644)
61+
if err != nil {
62+
return err
63+
}
64+
PrintCompletionLine("File saved successfully")
65+
return nil
5066
}
5167

5268
// DownloadSharedFile downloads a file using a share string (username:reference:sharepassword:base64filename)

utils/progress.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package utils
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
)
8+
9+
const (
10+
spinnerFrames = `⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏`
11+
barLength = 30
12+
)
13+
14+
var spinnerIdx = 0
15+
16+
// PrintProgress displays a formatted progress message with optional spinner
17+
func PrintProgress(message string, withSpinner bool) {
18+
if withSpinner {
19+
frame := string(spinnerFrames[spinnerIdx%len(spinnerFrames)])
20+
spinnerIdx++
21+
fmt.Printf("\r%s %s ", frame, message)
22+
} else {
23+
fmt.Printf("\r%s", message)
24+
}
25+
}
26+
27+
// PrintProgressBar displays a progress bar with percentage
28+
func PrintProgressBar(message string, current, total int) {
29+
if total <= 0 {
30+
total = 1
31+
}
32+
percent := (current * 100) / total
33+
filled := (current * barLength) / total
34+
35+
bar := "["
36+
for i := 0; i < barLength; i++ {
37+
if i < filled {
38+
bar += "="
39+
} else if i == filled {
40+
bar += ">"
41+
} else {
42+
bar += " "
43+
}
44+
}
45+
bar += "]"
46+
47+
fmt.Printf("\r%s %s %3d%%", message, bar, percent)
48+
}
49+
50+
// PrintProgressStep displays a step in a multi-step process
51+
func PrintProgressStep(step, totalSteps int, message string) {
52+
fmt.Printf("\r[%d/%d] %s", step, totalSteps, message)
53+
}
54+
55+
// ClearProgress clears the progress line
56+
func ClearProgress() {
57+
fmt.Print("\r" + strings.Repeat(" ", 100) + "\r")
58+
}
59+
60+
// PrintCompletionLine prints a completion message and clears progress
61+
func PrintCompletionLine(message string) {
62+
ClearProgress()
63+
fmt.Printf("✓ %s\n", message)
64+
}
65+
66+
// PrintErrorLine prints an error message
67+
func PrintErrorLine(message string) {
68+
ClearProgress()
69+
fmt.Printf("✗ %s\n", message)
70+
}
71+
72+
// SpinnerDelay returns a duration for smooth spinner animation
73+
func SpinnerDelay() time.Duration {
74+
return 80 * time.Millisecond
75+
}

utils/purge.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ func PurgeVault(session *Session) error {
1818
repoURL := fmt.Sprintf("git@github.com:%s/.zephyrus.git", session.Username)
1919

2020
// 1. Prepare an entirely new, empty Git environment in memory
21+
PrintProgressStep(1, 3, "Initializing purge...")
2122
storer := memory.NewStorage()
2223
fs := memfs.New()
2324

@@ -26,8 +27,10 @@ func PurgeVault(session *Session) error {
2627
return fmt.Errorf("failed to load private key: %w", err)
2728
}
2829
publicKeys.HostKeyCallback = cryptossh.InsecureIgnoreHostKey()
30+
PrintCompletionLine("Purge initialized")
2931

3032
// 2. Initialize a fresh repo and create a "Wipe" commit
33+
PrintProgressStep(2, 3, "Creating purge commit...")
3134
r, _ := git.Init(storer, fs)
3235
w, _ := r.Worktree()
3336

@@ -42,8 +45,10 @@ func PurgeVault(session *Session) error {
4245
if err != nil {
4346
return fmt.Errorf("failed to create purge commit: %w", err)
4447
}
48+
PrintCompletionLine("Purge commit created")
4549

4650
// 3. Force push this empty state to GitHub to overwrite everything
51+
PrintProgressStep(3, 3, "Force pushing to GitHub (wiping remote vault)...")
4752
_, _ = r.CreateRemote(&config.RemoteConfig{Name: "origin", URLs: []string{repoURL}})
4853

4954
err = r.Push(&git.PushOptions{
@@ -55,6 +60,7 @@ func PurgeVault(session *Session) error {
5560
if err != nil {
5661
return fmt.Errorf("failed to push purge: %w", err)
5762
}
63+
PrintCompletionLine("Vault purged successfully")
5864

5965
// 4. Update the session index in memory to be empty
6066
session.Index = NewIndex()

utils/share.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ func GenerateShareReferenceWithLength(length int) (string, error) {
3333
// The shared file stores only a pointer (storage ID + encrypted file key) instead of a copy
3434
func ShareFile(vaultPath string, sharePassword string, session *Session) (string, error) {
3535
// 1. Find the file entry in the index
36+
PrintProgressStep(1, 5, "Locating file in vault...")
3637
entry, err := session.Index.FindEntry(vaultPath)
3738
if err != nil {
3839
return "", fmt.Errorf("could not find file in vault: %w", err)
@@ -42,14 +43,18 @@ func ShareFile(vaultPath string, sharePassword string, session *Session) (string
4243
if entry.Type == "folder" {
4344
return "", fmt.Errorf("'%s' is a directory, you can only share individual files", vaultPath)
4445
}
46+
PrintCompletionLine("File located")
4547

4648
// 3. Generate a new reference with configurable length from settings
49+
PrintProgressStep(2, 5, "Generating share reference...")
4750
ref, err := GenerateShareReferenceWithLength(session.Settings.ShareHashLength)
4851
if err != nil {
4952
return "", fmt.Errorf("failed to generate share reference: %w", err)
5053
}
54+
PrintCompletionLine("Share reference generated: " + ref)
5155

5256
// 4. Decrypt the file key with vault password to get raw 32-byte key
57+
PrintProgressStep(3, 5, "Preparing file key...")
5358
fileKeyBytes, err := DecryptHexToBytes(entry.FileKey, session.Password)
5459
if err != nil {
5560
return "", fmt.Errorf("failed to decrypt file key: %w", err)
@@ -66,12 +71,15 @@ func ShareFile(vaultPath string, sharePassword string, session *Session) (string
6671
}
6772

6873
// 6. Encrypt the pointer with the share password
74+
PrintProgressStep(4, 5, "Encrypting share pointer...")
6975
pointerEncrypted, err := Encrypt(pointerJSON, sharePassword)
7076
if err != nil {
7177
return "", fmt.Errorf("failed to encrypt share pointer: %w", err)
7278
}
79+
PrintCompletionLine("Share pointer encrypted")
7380

7481
// 7. Upload pointer to /shared/{ref}
82+
PrintProgressStep(5, 5, "Uploading to GitHub...")
7583
sharedPath := fmt.Sprintf("shared/%s", ref)
7684
filesToPush := map[string][]byte{
7785
sharedPath: pointerEncrypted,
@@ -88,6 +96,7 @@ func ShareFile(vaultPath string, sharePassword string, session *Session) (string
8896
if err != nil {
8997
return "", fmt.Errorf("failed to upload share pointer: %w", err)
9098
}
99+
PrintCompletionLine("Share pointer uploaded to GitHub")
91100

92101
// 8. Add entry to shared index
93102
if session.SharedIndex == nil {

utils/upload.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,22 @@ import (
44
"encoding/hex"
55
"fmt"
66
"os"
7+
"time"
78
)
89

910
func UploadFile(sourcePath string, vaultPath string, session *Session) error {
1011
repoURL := fmt.Sprintf("git@github.com:%s/.zephyrus.git", session.Username)
1112

1213
// 1. Read source
14+
PrintProgressStep(1, 5, "Reading file...")
1315
data, err := os.ReadFile(sourcePath)
1416
if err != nil {
1517
return err
1618
}
19+
PrintCompletionLine("File read successfully")
1720

1821
// 2. Determine Storage Name and File Key
22+
PrintProgressStep(2, 5, "Validating vault...")
1923
var realName string
2024
var fileKey []byte
2125

@@ -48,20 +52,27 @@ func UploadFile(sourcePath string, vaultPath string, session *Session) error {
4852
session.Index.AddFile(vaultPath, realName, encryptedKeyHex)
4953
fmt.Printf("Uploading new file: %s as %s\n", vaultPath, realName)
5054
}
55+
PrintCompletionLine("File validated")
5156

5257
// 3. Encrypt file data with the per-file key
58+
PrintProgressStep(3, 5, "Encrypting file...")
59+
time.Sleep(time.Millisecond * 100) // Simulate work for visibility
5360
encryptedData, err := EncryptWithKey(data, fileKey)
5461
if err != nil {
5562
return err
5663
}
64+
PrintCompletionLine("File encrypted")
5765

5866
// 4. Encrypt updated index
67+
PrintProgressStep(4, 5, "Updating vault index...")
5968
indexBytes, err := session.Index.ToBytes(session.Password)
6069
if err != nil {
6170
return err
6271
}
72+
PrintCompletionLine("Vault index updated")
6373

6474
// 5. Push to Git
75+
PrintProgressStep(5, 5, "Uploading to GitHub...")
6576
filesToPush := map[string][]byte{
6677
realName: encryptedData,
6778
".config/index": indexBytes,
@@ -71,6 +82,7 @@ func UploadFile(sourcePath string, vaultPath string, session *Session) error {
7182
if err != nil {
7283
return err
7384
}
85+
PrintCompletionLine("Upload to GitHub completed")
7486

7587
// 6. Save updated index to local session to bypass cache
7688
return nil

0 commit comments

Comments
 (0)