-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore!(improv): add base client app and improving broker response (#3)
* feat(dev/cli): initialize cli * refactor(dev/cli): refactoring flag system * refactor(dev/cli): simplify print usage * feat(dev/cli): add custom http handler for verifying * feat(dev/server): add isPublic property on variable * feat(dev/cli): add status code validation for response * feat(dev/cli): add status code validation for response * feat(dev/cli): finalize auth command * feat(dev/cli): create base command for get * refactor(dev/cli): formatting auth and add remove default value * feat(dev/cli): create http request for get token before spawning echo instance * feat(dev/cli): implementing webserver on get command * refactor(dev/cli): pruning one file * feat(dev/image): add docker compose * fix(dev/server): modify dockerfile * fix(dev/server): pruning dockerfile and modify csproj * feat(dev/cli): add message on command before sending file * feat(dev/cli): add feature to upload * feat(dev/cli): add progress while uploading * feat(dev/cli): add slider for progressbar * feat(dev/cli): add validator for duplicate file * feat(dev/cli): set response header for keep-alive * refactor(dev/cli): remove unnecessary file * feat(dev/cli): create new base command for send * refactor(dev/server): add tunnel metadata on verify * feat(dev/server): improving entrypoint for creating base random passowrd for salting certificate (soon) * refactor(dev/server): add environment on docker compose * fix(dev/cli): change parsing and add missing parameter * fix(dev/cli): fixing wrong pinning json * feat(dev/cli): add ssh tunnel generator args and implement on get variable * feat(dev/cli): make function to upload file fix(dev/cli): create command for gracefully kill tunnel after transfer * fix(dev/cli): fix error when ssh tunnel is up * feat(dev/cli): progress bar for uploading fix(dev/cli): make receiver only retrive one request from sender
- Loading branch information
Showing
21 changed files
with
974 additions
and
33 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,89 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"flag" | ||
"fmt" | ||
"net/http" | ||
"os" | ||
|
||
internal "github.com/mplus-oss/mdrop/client/internal" | ||
) | ||
|
||
func AuthCommand(args []string) { | ||
flag := flag.NewFlagSet("mdrop auth", flag.ExitOnError) | ||
var ( | ||
url = flag.String("url", "https://example.com", "URL of broker") | ||
token = flag.String("token", "", "Private key token if the broker is on private mode") | ||
) | ||
flag.Parse(args) | ||
|
||
if *token == "" { | ||
*token = "token" | ||
} | ||
|
||
var client = &http.Client{} | ||
|
||
fmt.Println("Authenticating...") | ||
req, err := http.NewRequest("POST", *url+"/verify?token="+*token, nil) | ||
if err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
res, err := client.Do(req) | ||
if err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
defer res.Body.Close() | ||
|
||
var verifyData VerifyJSONReturn | ||
err = json.NewDecoder(res.Body).Decode(&verifyData) | ||
if err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
|
||
if res.StatusCode != 200 { | ||
var msg string | ||
if verifyData.ErrorTitle == "" { | ||
msg = verifyData.Message | ||
} else { | ||
msg = verifyData.ErrorTitle | ||
} | ||
fmt.Println("Error:", msg) | ||
os.Exit(1) | ||
} | ||
|
||
if verifyData.IsPublic { | ||
fmt.Println("This broker is running on public mode.") | ||
*token = "" | ||
} | ||
|
||
config := internal.ConfigFile{ | ||
URL: *url, | ||
Token: *token, | ||
Tunnel: internal.ConfigFileTunnel{ | ||
Host: verifyData.TunnelHost, | ||
Port: verifyData.TunnelPort, | ||
}, | ||
} | ||
err = config.WriteConfig() | ||
if err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
|
||
fmt.Println("Authenticated!") | ||
os.Exit(0) | ||
} | ||
|
||
type VerifyJSONReturn struct { | ||
Message string `json:"message"` | ||
IsPublic bool `json:"isPublic"` | ||
TunnelHost string `json:"tunnelHost"` | ||
TunnelPort int `json:"tunnelPort"` | ||
|
||
// This respon fired when the API is failed | ||
ErrorTitle string `json:"title"` | ||
} |
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,81 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"math/rand/v2" | ||
"os" | ||
|
||
internal "github.com/mplus-oss/mdrop/client/internal" | ||
) | ||
|
||
var errGetGlobal chan error = make(chan error) | ||
|
||
func GetCommand(args []string) { | ||
flag := flag.NewFlagSet("mdrop get", flag.ExitOnError) | ||
var ( | ||
expired = flag.Int("expired", 3, "Expired timeout in hours.") | ||
port = flag.Int("port", 0, "Specified port on broker. Range of port is about 10k - 59k. (default rand(10000, 59999))") | ||
localPort = flag.Int("localPort", 6000, "Specified port on local.") | ||
) | ||
flag.Parse(args) | ||
|
||
if *port == 0 { | ||
*port = randRange(10000, 59999) | ||
} | ||
if *port < 10000 || *port > 59999 { | ||
fmt.Println("Invalid port range!") | ||
os.Exit(1) | ||
} | ||
|
||
var c internal.ConfigFile | ||
err := c.ParseConfig(&c) | ||
if err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
|
||
fmt.Println("Note: Press Ctrl+C to close the session.") | ||
fmt.Println("Connecting to tunnel...") | ||
|
||
go StartShellTunnel(true, c, *localPort, *port) | ||
err = <- sshErrGlobal | ||
if err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
|
||
// Get token data | ||
fmt.Print("Creating the room...\n\n") | ||
var path = fmt.Sprintf( | ||
"%v/room/create?durationInHours=%v&port=%v", | ||
c.URL, | ||
*expired, | ||
*port, | ||
) | ||
roomData, err := getToken(path) | ||
if err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
fmt.Println(roomData.Token) | ||
|
||
// Start the Echo server | ||
go createWebserver(*localPort, roomData.Token) | ||
|
||
fmt.Println( | ||
"\nCopy this token to the sender. Make sure sender has authenticated to", | ||
c.URL, | ||
"before sending the file.", | ||
) | ||
|
||
err = <- errGetGlobal | ||
if err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func randRange(min, max int) int { | ||
return rand.IntN(max-min) + min | ||
} |
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,37 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"net/http" | ||
) | ||
|
||
type CreateRoomJSONReturn struct { | ||
Token string `json:"token"` | ||
|
||
// This respon fired when the API is failed | ||
ErrorTitle string `json:"title"` | ||
Errors struct { | ||
Token []string `json:"token"` | ||
} `json:"errors"` | ||
} | ||
|
||
func getToken(path string) (CreateRoomJSONReturn, error) { | ||
var client = &http.Client{} | ||
req, err := http.NewRequest("POST", path, nil) | ||
if err != nil { | ||
return CreateRoomJSONReturn{} ,err | ||
} | ||
res, err := client.Do(req) | ||
if err != nil { | ||
return CreateRoomJSONReturn{} ,err | ||
} | ||
defer res.Body.Close() | ||
|
||
var roomData CreateRoomJSONReturn | ||
err = json.NewDecoder(res.Body).Decode(&roomData) | ||
if err != nil { | ||
return CreateRoomJSONReturn{} ,err | ||
} | ||
|
||
return roomData, nil | ||
} |
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,137 @@ | ||
package main | ||
|
||
import ( | ||
"bufio" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"os" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/labstack/echo/v4" | ||
"github.com/schollz/progressbar/v3" | ||
) | ||
|
||
func createWebserver(port int, token string) { | ||
reader := bufio.NewReader(os.Stdin) | ||
isInProgress := false | ||
e := echo.New() | ||
e.HideBanner = true | ||
e.HidePort = true | ||
|
||
e.POST("/receive", func (c echo.Context) error { | ||
// Set keep alive | ||
c.Response().Header().Set("Connection", "Keep-Alive") | ||
c.Response().Header().Set("Keep-Alive", "timeout=5,max=0") | ||
|
||
// If there's upload on progress | ||
if isInProgress { | ||
return c.JSON(http.StatusBadRequest, map[string]string{ | ||
"message": "There's some process on receiver server", | ||
}) | ||
} | ||
|
||
// Get query token | ||
if queryToken := c.QueryParam("token"); queryToken != token { | ||
return c.JSON(http.StatusBadRequest, map[string]string{ | ||
"message": "Empty or invalid token!", | ||
}) | ||
} | ||
|
||
// Get file | ||
file, err := c.FormFile("file") | ||
if err != nil { | ||
return c.JSON(http.StatusBadRequest, map[string]string{ | ||
"message": "Missing file from FormData", | ||
}) | ||
} | ||
|
||
// Prompting | ||
isInProgress = true | ||
fmt.Println("\nIncoming file:", file.Filename) | ||
fmt.Print("Accept? [(Y)es/(N)o] -> ") | ||
prompt, err := reader.ReadString('\n') | ||
if err != nil { | ||
return validateInternalError(c, err) | ||
} | ||
prompt = strings.Replace(prompt, "\n", "", -1) | ||
if strings.ToLower(prompt) != "y" { | ||
isInProgress = false | ||
return c.JSON(http.StatusUnauthorized, map[string]string{ | ||
"message": "File declined from receiver", | ||
}) | ||
} | ||
|
||
// Validate if the data can be override or not | ||
currentPath, err := os.Getwd() | ||
if err != nil { | ||
return validateInternalError(c, err) | ||
} | ||
currentPath += "/"+file.Filename | ||
if fileStatus, _ := os.Stat(currentPath); fileStatus != nil { | ||
fmt.Print("There's duplicate file. Action? [(Y)es/(N)o/(R)ename] -> ") | ||
prompt, err = reader.ReadString('\n') | ||
if err != nil { | ||
return validateInternalError(c, err) | ||
} | ||
prompt = strings.Replace(prompt, "\n", "", -1) | ||
if strings.ToLower(prompt) == "n" { | ||
isInProgress = false | ||
return c.JSON(http.StatusUnauthorized, map[string]string{ | ||
"message": "File declined from receiver", | ||
}) | ||
} | ||
if strings.ToLower(prompt) == "r" { | ||
fmt.Print("Change filename ["+file.Filename+"] -> ") | ||
prompt, err = reader.ReadString('\n') | ||
if err != nil { | ||
return validateInternalError(c, err) | ||
} | ||
prompt = strings.Replace(prompt, "\n", "", -1) | ||
if prompt == file.Filename { | ||
fmt.Println("Canceled. Duplicate file.") | ||
isInProgress = false | ||
return c.JSON(http.StatusUnauthorized, map[string]string{ | ||
"message": "Duplicate file from receiver", | ||
}) | ||
} | ||
file.Filename = prompt | ||
} | ||
} | ||
|
||
// File scanning | ||
src, err := file.Open() | ||
if err != nil { | ||
return validateInternalError(c, err) | ||
} | ||
dst, err := os.Create(file.Filename) | ||
if err != nil { | ||
return validateInternalError(c, err) | ||
} | ||
defer src.Close() | ||
defer dst.Close() | ||
|
||
// Copy file | ||
bar := progressbar.DefaultBytes(file.Size, file.Filename) | ||
if _, err = io.Copy(io.MultiWriter(dst, bar), src); err != nil { | ||
return validateInternalError(c, err) | ||
} | ||
|
||
// Complete | ||
isInProgress = false | ||
return c.JSON(http.StatusOK, map[string]string{ | ||
"message": "ok", | ||
}) | ||
}) | ||
|
||
e.Logger.Fatal(e.Start("0.0.0.0:"+strconv.Itoa(port))) | ||
errGetGlobal <- errors.New("Webserver Closed") | ||
} | ||
|
||
func validateInternalError(c echo.Context, err error) error { | ||
return c.JSON(http.StatusInternalServerError, map[string]string{ | ||
"message": err.Error(), | ||
}) | ||
} |
Oops, something went wrong.