diff options
| -rwxr-xr-x | client/client | bin | 0 -> 7440474 bytes | |||
| -rw-r--r-- | client/main.go | 196 | ||||
| -rw-r--r-- | readme.md | 30 |
3 files changed, 203 insertions, 23 deletions
diff --git a/client/client b/client/client Binary files differnew file mode 100755 index 0000000..5b43e40 --- /dev/null +++ b/client/client diff --git a/client/main.go b/client/main.go new file mode 100644 index 0000000..279c88d --- /dev/null +++ b/client/main.go @@ -0,0 +1,196 @@ +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io" + "log" + "net/http" + "os" + "path/filepath" + + "gopkg.in/yaml.v3" +) + +var ConfigFn = filepath.Join(os.Getenv("HOME"), ".paste") +var TokenFN = filepath.Join(os.Getenv("HOME"), ".paste-token") + +var logger = log.New(os.Stderr, "", 0) +var httpC = &http.Client{} + +type Paste struct { + Id string + Title string + Tags map[string]struct{} + Content string +} + +type Config struct { + Username string + Password string + Hostname string +} + +func loadConfig(fn string) (*Config, error) { + fh, err := os.Open(fn) + if err != nil { + return nil, err + } + dec := yaml.NewDecoder(fh) + dec.KnownFields(true) + + c := &Config{} + err = dec.Decode(c) + + return c, err +} + +func (c *Config) NoTokenReq(method, pth string, data interface{}) (*http.Response, error) { + b, err := json.Marshal(data) + if err != nil { + return nil, err + } + + buf := bytes.NewBuffer(b) + + req, err := http.NewRequest(method, c.Hostname+pth, buf) + if err != nil { + logger.Fatal(err) + } + req.Header.Add("Content-type", "application/json") + + resp, err := httpC.Do(req) + if err != nil { + logger.Fatal(err) + } + + return resp, err +} + +func (c *Config) Req(method, pth string, data interface{}) (*http.Response, error) { + b, err := json.Marshal(data) + if err != nil { + return nil, err + } + + buf := bytes.NewBuffer(b) + + req, err := http.NewRequest(method, c.Hostname+pth, buf) + if err != nil { + logger.Fatal(err) + } + req.Header.Add("Authorization", "Bearer "+c.Token()) + req.Header.Add("Content-type", "application/json") + + resp, err := httpC.Do(req) + if err != nil { + logger.Fatal(err) + } + + return resp, err +} + +func (c *Config) Token() string { + s, err := c.GetToken() + if err != nil { + logger.Fatal(err) + } + return s +} + +func (c *Config) GetToken() (string, error) { + resp, err := c.NoTokenReq("POST", "/login", map[string]string{ + "Username": c.Username, + "Password": c.Password, + }) + if err != nil { + return "", err + } + + respData := map[string]string{} + + dec := json.NewDecoder(resp.Body) + err = dec.Decode(&respData) + if err != nil { + return "", err + } + + token, ok := respData["token"] + if !ok { + logger.Fatal("Cannot have empty token") + } + + // logger.Println(token) + + return token, nil +} + +func (c *Config) NewPaste(title string) { + buf := &bytes.Buffer{} + + _, err := io.Copy(buf, os.Stdin) + if err != nil { + logger.Fatal(err) + } + + p := &Paste{ + Title: title, + Content: buf.String(), + } + + resp, err := c.Req("POST", "/new", p) + if err != nil { + logger.Fatal(err) + } + + if resp.StatusCode != 200 { + io.Copy(os.Stderr, resp.Body) + os.Exit(1) + } + + out := map[string]string{} + + dec := json.NewDecoder(resp.Body) + err = dec.Decode(&out) + if err != nil { + logger.Println(err) + } + + fmt.Printf("%s/view/%s\n", c.Hostname, out["id"]) +} + +func main() { + fl := flag.NewFlagSet("simple pastebin client", flag.ExitOnError) + + fl.StringVar(&ConfigFn, "c", ConfigFn, "Configuration file") + title := fl.String("t", "", "Optional title for the message") + debug := fl.Bool("d", false, "debugging add information to the logging output DEBUG=true|false controls this as well") + + _ = fl.Parse(os.Args[1:]) + + if d := os.Getenv("DEBUG"); d == "true" || *debug { + logger.SetFlags(log.LstdFlags | log.Llongfile) + } + + conf, err := loadConfig(ConfigFn) + if err != nil { + logger.Println(err) + + fmt.Println("") + fmt.Println("//") + fmt.Println("// Example configuration file") + enc := yaml.NewEncoder(os.Stdout) + enc.SetIndent(2) + enc.Encode(&Config{ + Username: "changeme", + Password: "changeme", + Hostname: "https://example.com/paste", + }) + + os.Exit(1) + } + + conf.NewPaste(*title) +} @@ -1,27 +1,11 @@ -# Brutally simple pastebin +# Simple pastebin -Decided to challenge myself to write a really simple pastebin in go, utilizing -almost nothing but the standard library in less than an hour. +Very much a work in progress. -This is the result of about an hour and a half of work. +There's a simple server and client. -It will create a binary with embedded templates. Simply point it at the -directory in which you'd like to store your pastes. +The server does require authorization and has some minimal documentation +when you fire it up. -Each paste is a file, a random id assigned at creation. There's no way to edit -or change a paste. The only way to delete it is to remove it from the -filesystem. - -There's no indexing function or search either. - -There's also absolutely no protection against people spamming the API until -your disk is full, no authentication either. - -If you have go already installed and properly configured on your system and -would like to mess with it: - -``` -go get riedstra.dev/mitch/bpaste -``` - -Will install it. +The client require a configuration file in your home folder and outputs +the URL to the paste if successful. |
