Page MenuHomeMusing Studio

No OneTemporary

diff --git a/api/api.go b/api/api.go
index b309993..31e90e7 100644
--- a/api/api.go
+++ b/api/api.go
@@ -1,267 +1,268 @@
package api
import (
"fmt"
"github.com/atotto/clipboard"
writeas "github.com/writeas/go-writeas/v2"
"github.com/writeas/web-core/posts"
"github.com/writeas/writeas-cli/config"
+ "github.com/writeas/writeas-cli/executable"
"github.com/writeas/writeas-cli/log"
cli "gopkg.in/urfave/cli.v1"
)
func newClient(c *cli.Context, authRequired bool) (*writeas.Client, error) {
var client *writeas.Client
var clientConfig writeas.Config
cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
if err != nil {
return nil, fmt.Errorf("Failed to load configuration file: %v", err)
}
if c.GlobalString("host") != "" {
clientConfig.URL = c.GlobalString("host") + "/api"
} else if cfg.Default.Host != "" {
clientConfig.URL = cfg.Default.Host + "/api"
} else if config.IsDev() {
clientConfig.URL = config.DevBaseURL + "/api"
} else {
clientConfig.URL = config.WriteasBaseURL + "/api"
}
if config.IsTor(c) {
clientConfig.URL = config.TorURL(c)
clientConfig.TorPort = config.TorPort(c)
}
client = writeas.NewClientWith(clientConfig)
client.UserAgent = config.UserAgent(c)
// TODO: load user into var shared across the app
u, _ := config.LoadUser(c)
if u != nil {
client.SetToken(u.AccessToken)
} else if authRequired {
- return nil, fmt.Errorf("Not currently logged in. Authenticate with: writeas auth <username>")
+ return nil, fmt.Errorf("Not currently logged in. Authenticate with: " + executable.Name() + " auth <username>")
}
return client, nil
}
// DoFetch retrieves the Write.as post with the given friendlyID,
// optionally via the Tor hidden service.
func DoFetch(c *cli.Context, friendlyID string) error {
cl, err := newClient(c, false)
if err != nil {
return err
}
p, err := cl.GetPost(friendlyID)
if err != nil {
return err
}
if p.Title != "" {
fmt.Printf("# %s\n\n", string(p.Title))
}
fmt.Printf("%s\n", string(p.Content))
return nil
}
// DoFetchPosts retrieves all remote posts for the
// authenticated user
func DoFetchPosts(c *cli.Context) ([]writeas.Post, error) {
cl, err := newClient(c, true)
if err != nil {
return nil, fmt.Errorf("%v", err)
}
posts, err := cl.GetUserPosts()
if err != nil {
return nil, err
}
return *posts, nil
}
// DoPost creates a Write.as post, returning an error if it was
// unsuccessful.
func DoPost(c *cli.Context, post []byte, font string, encrypt, code bool) (*writeas.Post, error) {
cl, err := newClient(c, false)
if err != nil {
return nil, fmt.Errorf("%v", err)
}
pp := &writeas.PostParams{
Font: config.GetFont(code, font),
Collection: config.Collection(c),
}
pp.Title, pp.Content = posts.ExtractTitle(string(post))
if lang := config.Language(c, true); lang != "" {
pp.Language = &lang
}
p, err := cl.CreatePost(pp)
if err != nil {
return nil, fmt.Errorf("Unable to post: %v", err)
}
cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
if err != nil {
return nil, fmt.Errorf("Couldn't check for config file: %v", err)
}
var url string
if p.Collection != nil {
url = p.Collection.URL + p.Slug
} else {
if c.GlobalString("host") != "" {
url = c.GlobalString("host")
} else if cfg.Default.Host != "" {
url = cfg.Default.Host
} else if config.IsDev() {
url = config.DevBaseURL
} else if config.IsTor(c) {
url = config.TorBaseURL
} else {
url = config.WriteasBaseURL
}
url += "/" + p.ID
// Output URL in requested format
if c.Bool("md") {
url += ".md"
}
}
if cl.Token() == "" {
// Store post locally, since we're not authenticated
AddPost(c, p.ID, p.Token)
}
// Copy URL to clipboard
err = clipboard.WriteAll(string(url))
if err != nil {
- log.Errorln("writeas: Didn't copy to clipboard: %s", err)
+ log.Errorln(executable.Name()+": Didn't copy to clipboard: %s", err)
} else {
log.Info(c, "Copied to clipboard.")
}
// Output URL
fmt.Printf("%s\n", url)
return p, nil
}
// DoFetchCollections retrieves a list of the currently logged in users
// collections.
func DoFetchCollections(c *cli.Context) ([]RemoteColl, error) {
cl, err := newClient(c, true)
if err != nil {
if config.Debug() {
log.ErrorlnQuit("could not create client: %v", err)
}
return nil, fmt.Errorf("Couldn't create new client")
}
colls, err := cl.GetUserCollections()
if err != nil {
if config.Debug() {
log.ErrorlnQuit("failed fetching user collections: %v", err)
}
return nil, fmt.Errorf("Couldn't get user blogs")
}
out := make([]RemoteColl, len(*colls))
for i, c := range *colls {
coll := RemoteColl{
Alias: c.Alias,
Title: c.Title,
URL: c.URL,
}
out[i] = coll
}
return out, nil
}
// DoUpdate updates the given post on Write.as.
func DoUpdate(c *cli.Context, post []byte, friendlyID, token, font string, code bool) error {
cl, err := newClient(c, false)
if err != nil {
return fmt.Errorf("%v", err)
}
params := writeas.PostParams{}
params.Title, params.Content = posts.ExtractTitle(string(post))
if lang := config.Language(c, false); lang != "" {
params.Language = &lang
}
if code || font != "" {
params.Font = config.GetFont(code, font)
}
_, err = cl.UpdatePost(friendlyID, token, &params)
if err != nil {
if config.Debug() {
log.ErrorlnQuit("Problem updating: %v", err)
}
return fmt.Errorf("Post doesn't exist, or bad edit token given.")
}
return nil
}
// DoDelete deletes the given post on Write.as, and removes any local references
func DoDelete(c *cli.Context, friendlyID, token string) error {
cl, err := newClient(c, false)
if err != nil {
return fmt.Errorf("%v", err)
}
err = cl.DeletePost(friendlyID, token)
if err != nil {
if config.Debug() {
log.ErrorlnQuit("Problem deleting: %v", err)
}
return fmt.Errorf("Post doesn't exist, or bad edit token given.")
}
RemovePost(c, friendlyID)
return nil
}
func DoLogIn(c *cli.Context, username, password string) error {
cl, err := newClient(c, false)
if err != nil {
return fmt.Errorf("%v", err)
}
u, err := cl.LogIn(username, password)
if err != nil {
if config.Debug() {
log.ErrorlnQuit("Problem logging in: %v", err)
}
return err
}
err = config.SaveUser(c, u)
if err != nil {
return err
}
log.Info(c, "Logged in as %s.\n", u.User.Username)
return nil
}
func DoLogOut(c *cli.Context) error {
cl, err := newClient(c, true)
if err != nil {
return fmt.Errorf("%v", err)
}
err = cl.LogOut()
if err != nil {
if config.Debug() {
log.ErrorlnQuit("Problem logging out: %v", err)
}
return err
}
// delete local user file
return config.DeleteUser(c)
}
diff --git a/commands/commands.go b/commands/commands.go
index bd6a493..41b0662 100644
--- a/commands/commands.go
+++ b/commands/commands.go
@@ -1,410 +1,411 @@
package commands
import (
"fmt"
"io/ioutil"
"os"
"text/tabwriter"
"github.com/howeyc/gopass"
"github.com/writeas/writeas-cli/api"
"github.com/writeas/writeas-cli/config"
+ "github.com/writeas/writeas-cli/executable"
"github.com/writeas/writeas-cli/log"
cli "gopkg.in/urfave/cli.v1"
)
func CmdPost(c *cli.Context) error {
if config.IsTor(c) {
log.Info(c, "Publishing via hidden service...")
} else {
log.Info(c, "Publishing...")
}
_, err := api.DoPost(c, api.ReadStdIn(), c.String("font"), false, c.Bool("code"))
if err != nil {
return cli.NewExitError(err.Error(), 1)
}
return nil
}
func CmdNew(c *cli.Context) error {
fname, p := api.ComposeNewPost()
if p == nil {
// Assume composeNewPost already told us what the error was. Abort now.
os.Exit(1)
}
// Ensure we have something to post
if len(*p) == 0 {
// Clean up temporary post
if fname != "" {
os.Remove(fname)
}
log.InfolnQuit("Empty post. Bye!")
}
if config.IsTor(c) {
log.Info(c, "Publishing via hidden service...")
} else {
log.Info(c, "Publishing...")
}
_, err := api.DoPost(c, *p, c.String("font"), false, c.Bool("code"))
if err != nil {
log.Errorln("Error posting: %s\n%s", err, config.MessageRetryCompose(fname))
return cli.NewExitError("", 1)
}
// Clean up temporary post
if fname != "" {
os.Remove(fname)
}
return nil
}
func CmdPublish(c *cli.Context) error {
filename := c.Args().Get(0)
if filename == "" {
- return cli.NewExitError("usage: writeas publish <filename>", 1)
+ return cli.NewExitError("usage: "+executable.Name()+" publish <filename>", 1)
}
content, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
if config.IsTor(c) {
log.Info(c, "Publishing via hidden service...")
} else {
log.Info(c, "Publishing...")
}
_, err = api.DoPost(c, content, c.String("font"), false, c.Bool("code"))
if err != nil {
return cli.NewExitError(err.Error(), 1)
}
// TODO: write local file if directory is set
return nil
}
func CmdDelete(c *cli.Context) error {
friendlyID := c.Args().Get(0)
token := c.Args().Get(1)
if friendlyID == "" {
- return cli.NewExitError("usage: writeas delete <postId> [<token>]", 1)
+ return cli.NewExitError("usage: "+executable.Name()+" delete <postId> [<token>]", 1)
}
u, _ := config.LoadUser(c)
if token == "" {
// Search for the token locally
token = api.TokenFromID(c, friendlyID)
if token == "" && u == nil {
log.Errorln("Couldn't find an edit token locally. Did you create this post here?")
- log.ErrorlnQuit("If you have an edit token, use: writeas delete %s <token>", friendlyID)
+ log.ErrorlnQuit("If you have an edit token, use: "+executable.Name()+" delete %s <token>", friendlyID)
}
}
if config.IsTor(c) {
log.Info(c, "Deleting via hidden service...")
} else {
log.Info(c, "Deleting...")
}
err := api.DoDelete(c, friendlyID, token)
if err != nil {
return cli.NewExitError(fmt.Sprintf("Couldn't delete post: %v", err), 1)
}
// TODO: Delete local file, if necessary
return nil
}
func CmdUpdate(c *cli.Context) error {
friendlyID := c.Args().Get(0)
token := c.Args().Get(1)
if friendlyID == "" {
- return cli.NewExitError("usage: writeas update <postId> [<token>]", 1)
+ return cli.NewExitError("usage: "+executable.Name()+" update <postId> [<token>]", 1)
}
u, _ := config.LoadUser(c)
if token == "" {
// Search for the token locally
token = api.TokenFromID(c, friendlyID)
if token == "" && u == nil {
log.Errorln("Couldn't find an edit token locally. Did you create this post here?")
- log.ErrorlnQuit("If you have an edit token, use: writeas update %s <token>", friendlyID)
+ log.ErrorlnQuit("If you have an edit token, use: "+executable.Name()+" update %s <token>", friendlyID)
}
}
// Read post body
fullPost := api.ReadStdIn()
if config.IsTor(c) {
log.Info(c, "Updating via hidden service...")
} else {
log.Info(c, "Updating...")
}
err := api.DoUpdate(c, fullPost, friendlyID, token, c.String("font"), c.Bool("code"))
if err != nil {
return cli.NewExitError(fmt.Sprintf("%v", err), 1)
}
return nil
}
func CmdGet(c *cli.Context) error {
friendlyID := c.Args().Get(0)
if friendlyID == "" {
- return cli.NewExitError("usage: writeas get <postId>", 1)
+ return cli.NewExitError("usage: "+executable.Name()+" get <postId>", 1)
}
if config.IsTor(c) {
log.Info(c, "Getting via hidden service...")
} else {
log.Info(c, "Getting...")
}
err := api.DoFetch(c, friendlyID)
if err != nil {
return cli.NewExitError(fmt.Sprintf("%v", err), 1)
}
return nil
}
func CmdAdd(c *cli.Context) error {
friendlyID := c.Args().Get(0)
token := c.Args().Get(1)
if friendlyID == "" || token == "" {
- return cli.NewExitError("usage: writeas add <postId> <token>", 1)
+ return cli.NewExitError("usage: "+executable.Name()+" add <postId> <token>", 1)
}
err := api.AddPost(c, friendlyID, token)
if err != nil {
return cli.NewExitError(fmt.Sprintf("%v", err), 1)
}
return nil
}
func CmdListPosts(c *cli.Context) error {
urls := c.Bool("url")
ids := c.Bool("id")
details := c.Bool("v")
posts := api.GetPosts(c)
u, _ := config.LoadUser(c)
if u != nil {
if config.IsTor(c) {
log.Info(c, "Getting posts via hidden service...")
} else {
log.Info(c, "Getting posts...")
}
remotePosts, err := api.GetUserPosts(c, true)
if err != nil {
return cli.NewExitError(fmt.Sprintf("error getting posts: %v", err), 1)
}
if len(remotePosts) > 0 {
fmt.Println("Anonymous Posts")
if details {
identifier := "URL"
if ids || !urls {
identifier = "ID"
}
fmt.Println(identifier)
}
}
for _, p := range remotePosts {
identifier := getPostURL(c, p.ID)
if ids || !urls {
identifier = p.ID
}
fmt.Println(identifier)
}
if len(*posts) > 0 {
fmt.Printf("\nUnclaimed Posts\n")
}
}
if details {
var p api.Post
tw := tabwriter.NewWriter(os.Stdout, 10, 0, 2, ' ', tabwriter.TabIndent)
numPosts := len(*posts)
if ids || !urls && numPosts != 0 {
fmt.Fprintf(tw, "%s\t%s\t\n", "ID", "Token")
} else if numPosts != 0 {
fmt.Fprintf(tw, "%s\t%s\t\n", "URL", "Token")
} else {
fmt.Fprintf(tw, "No local posts found\n")
}
for i := range *posts {
p = (*posts)[numPosts-1-i]
if ids || !urls {
fmt.Fprintf(tw, "%s\t%s\t\n", p.ID, p.EditToken)
} else {
fmt.Fprintf(tw, "%s\t%s\t\n", getPostURL(c, p.ID), p.EditToken)
}
}
return tw.Flush()
}
for _, p := range *posts {
if ids || !urls {
fmt.Printf("%s\n", p.ID)
} else {
fmt.Printf("%s\n", getPostURL(c, p.ID))
}
}
return nil
}
func getPostURL(c *cli.Context, slug string) string {
base := config.WriteasBaseURL
if config.IsDev() {
base = config.DevBaseURL
}
ext := ""
// Output URL in requested format
if c.Bool("md") {
ext = ".md"
}
return fmt.Sprintf("%s/%s%s", base, slug, ext)
}
func CmdCollections(c *cli.Context) error {
u, err := config.LoadUser(c)
if err != nil {
return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1)
}
if u == nil {
- return cli.NewExitError("You must be authenticated to view collections.\nLog in first with: writeas auth <username>", 1)
+ return cli.NewExitError("You must be authenticated to view collections.\nLog in first with: "+executable.Name()+" auth <username>", 1)
}
if config.IsTor(c) {
log.Info(c, "Getting blogs via hidden service...")
} else {
log.Info(c, "Getting blogs...")
}
colls, err := api.DoFetchCollections(c)
if err != nil {
return cli.NewExitError(fmt.Sprintf("Couldn't get collections for user %s: %v", u.User.Username, err), 1)
}
urls := c.Bool("url")
tw := tabwriter.NewWriter(os.Stdout, 8, 0, 2, ' ', tabwriter.TabIndent)
detail := "Title"
if urls {
detail = "URL"
}
fmt.Fprintf(tw, "%s\t%s\t\n", "Alias", detail)
for _, c := range colls {
dData := c.Title
if urls {
dData = c.URL
}
fmt.Fprintf(tw, "%s\t%s\t\n", c.Alias, dData)
}
tw.Flush()
return nil
}
func CmdClaim(c *cli.Context) error {
u, err := config.LoadUser(c)
if err != nil {
return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1)
}
if u == nil {
- return cli.NewExitError("You must be authenticated to claim local posts.\nLog in first with: writeas auth <username>", 1)
+ return cli.NewExitError("You must be authenticated to claim local posts.\nLog in first with: "+executable.Name()+" auth <username>", 1)
}
localPosts := api.GetPosts(c)
if len(*localPosts) == 0 {
return nil
}
if config.IsTor(c) {
log.Info(c, "Claiming %d post(s) for %s via hidden service...", len(*localPosts), u.User.Username)
} else {
log.Info(c, "Claiming %d post(s) for %s...", len(*localPosts), u.User.Username)
}
results, err := api.ClaimPosts(c, localPosts)
if err != nil {
return cli.NewExitError(fmt.Sprintf("Failed to claim posts: %v", err), 1)
}
var okCount, errCount int
for _, r := range *results {
id := r.ID
if id == "" {
// No top-level ID, so the claim was successful
id = r.Post.ID
}
status := fmt.Sprintf("Post %s...", id)
if r.ErrorMessage != "" {
log.Errorln("%serror: %v", status, r.ErrorMessage)
errCount++
} else {
log.Info(c, "%sOK", status)
okCount++
// only delete local if successful
api.RemovePost(c, id)
}
}
log.Info(c, "%d claimed, %d failed", okCount, errCount)
return nil
}
func CmdAuth(c *cli.Context) error {
// Check configuration
u, err := config.LoadUser(c)
if err != nil {
return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1)
}
if u != nil && u.AccessToken != "" {
- return cli.NewExitError("You're already authenticated as "+u.User.Username+". Log out with: writeas logout", 1)
+ return cli.NewExitError("You're already authenticated as "+u.User.Username+". Log out with: "+executable.Name()+" logout", 1)
}
// Validate arguments and get password
// TODO: after global config, check for default user
username := c.Args().Get(0)
if username == "" {
- return cli.NewExitError("usage: writeas auth <username>", 1)
+ return cli.NewExitError("usage: "+executable.Name()+" auth <username>", 1)
}
fmt.Print("Password: ")
pass, err := gopass.GetPasswdMasked()
if err != nil {
return cli.NewExitError(fmt.Sprintf("error reading password: %v", err), 1)
}
// Validate password
if len(pass) == 0 {
return cli.NewExitError("Please enter your password.", 1)
}
if config.IsTor(c) {
log.Info(c, "Logging in via hidden service...")
} else {
log.Info(c, "Logging in...")
}
err = api.DoLogIn(c, username, string(pass))
if err != nil {
return cli.NewExitError(fmt.Sprintf("error logging in: %v", err), 1)
}
return nil
}
func CmdLogOut(c *cli.Context) error {
if config.IsTor(c) {
log.Info(c, "Logging out via hidden service...")
} else {
log.Info(c, "Logging out...")
}
err := api.DoLogOut(c)
if err != nil {
return cli.NewExitError(fmt.Sprintf("error logging out: %v", err), 1)
}
return nil
}
diff --git a/config/files_nix.go b/config/files_nix.go
index 0c10f04..13fb338 100644
--- a/config/files_nix.go
+++ b/config/files_nix.go
@@ -1,43 +1,44 @@
// +build !windows
package config
import (
"fmt"
"os/exec"
homedir "github.com/mitchellh/go-homedir"
+ "github.com/writeas/writeas-cli/executable"
)
const (
NoEditorErr = "Couldn't find default editor. Try setting $EDITOR environment variable in ~/.profile"
)
func parentDataDir() string {
dir, err := homedir.Dir()
if err != nil {
panic(err)
}
return dir
}
func EditPostCmd(fname string) *exec.Cmd {
editor := GetConfiguredEditor()
if editor == "" {
// Fall back to default editor
path, err := exec.LookPath("vim")
if err != nil {
path, err = exec.LookPath("nano")
if err != nil {
return nil
}
}
editor = path
}
return exec.Command(editor, fname)
}
func MessageRetryCompose(fname string) string {
- return fmt.Sprintf("To retry this post, run:\n cat %s | writeas", fname)
+ return fmt.Sprintf("To retry this post, run:\n cat %s | %s", fname, executable.Name())
}
diff --git a/config/files_win.go b/config/files_win.go
index 026b803..db2f459 100644
--- a/config/files_win.go
+++ b/config/files_win.go
@@ -1,26 +1,28 @@
// +build windows
package config
import (
"fmt"
"os"
"os/exec"
+
+ "github.com/writeas/writeas-cli/executable"
)
const (
NoEditorErr = "Error getting default editor. You shouldn't see this, so let us know you did: hello@write.as"
)
func parentDataDir() string {
return os.Getenv("APPDATA")
}
func EditPostCmd(fname string) *exec.Cmd {
// NOTE this won't work if fname contains spaces.
return exec.Command("cmd", "/C copy con "+fname)
}
func MessageRetryCompose(fname string) string {
- return fmt.Sprintf("To retry this post, run:\n type %s | writeas.exe", fname)
+ return fmt.Sprintf("To retry this post, run:\n type %s | %s", fname, executable.Name())
}
diff --git a/executable/executable.go b/executable/executable.go
new file mode 100644
index 0000000..697f419
--- /dev/null
+++ b/executable/executable.go
@@ -0,0 +1,13 @@
+// Package executable holds utility functions that assist both CLI executables,
+// writeas and wf.
+package executable
+
+import (
+ "os"
+ "path"
+)
+
+func Name() string {
+ n := os.Args[0]
+ return path.Base(n)
+}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 17, 8:55 PM (1 d, 7 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3324394

Event Timeline