2019-05-28 17:44:50 +00:00
|
|
|
package commands
|
2016-02-23 16:02:48 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2018-09-16 00:29:15 +00:00
|
|
|
"io/ioutil"
|
2016-02-23 16:02:48 +00:00
|
|
|
"os"
|
2018-09-19 00:55:00 +00:00
|
|
|
"path/filepath"
|
2019-05-30 20:37:07 +00:00
|
|
|
"text/tabwriter"
|
2018-12-17 15:49:15 +00:00
|
|
|
|
|
|
|
"github.com/howeyc/gopass"
|
2019-05-28 17:44:50 +00:00
|
|
|
"github.com/writeas/writeas-cli/api"
|
2019-05-28 00:18:45 +00:00
|
|
|
"github.com/writeas/writeas-cli/config"
|
2018-12-17 15:49:15 +00:00
|
|
|
"github.com/writeas/writeas-cli/fileutils"
|
2019-05-28 17:44:50 +00:00
|
|
|
"github.com/writeas/writeas-cli/log"
|
2018-12-17 15:49:15 +00:00
|
|
|
cli "gopkg.in/urfave/cli.v1"
|
2016-02-23 16:02:48 +00:00
|
|
|
)
|
|
|
|
|
2019-05-28 00:18:45 +00:00
|
|
|
func CmdPost(c *cli.Context) error {
|
2019-05-28 17:44:50 +00:00
|
|
|
_, err := api.HandlePost(api.ReadStdIn(), c)
|
2017-04-13 04:02:37 +00:00
|
|
|
return err
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 00:18:45 +00:00
|
|
|
func CmdNew(c *cli.Context) error {
|
2019-05-28 17:44:50 +00:00
|
|
|
fname, p := api.ComposeNewPost()
|
2016-02-23 16:02:48 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2019-05-28 17:44:50 +00:00
|
|
|
log.InfolnQuit("Empty post. Bye!")
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 17:44:50 +00:00
|
|
|
_, err := api.HandlePost(*p, c)
|
2016-02-23 16:02:48 +00:00
|
|
|
if err != nil {
|
2019-05-28 17:44:50 +00:00
|
|
|
log.Errorln("Error posting: %s\n%s", err, config.MessageRetryCompose(fname))
|
2017-04-13 04:02:37 +00:00
|
|
|
return cli.NewExitError("", 1)
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Clean up temporary post
|
|
|
|
if fname != "" {
|
|
|
|
os.Remove(fname)
|
|
|
|
}
|
2017-04-13 04:02:37 +00:00
|
|
|
|
|
|
|
return nil
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 00:18:45 +00:00
|
|
|
func CmdPublish(c *cli.Context) error {
|
2018-09-16 00:29:15 +00:00
|
|
|
filename := c.Args().Get(0)
|
|
|
|
if filename == "" {
|
|
|
|
return cli.NewExitError("usage: writeas publish <filename>", 1)
|
|
|
|
}
|
|
|
|
content, err := ioutil.ReadFile(filename)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-05-28 17:44:50 +00:00
|
|
|
p, err := api.HandlePost(content, c)
|
2018-09-19 00:38:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save post to posts folder
|
2019-05-29 00:05:36 +00:00
|
|
|
cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
|
2018-09-19 00:38:02 +00:00
|
|
|
if cfg.Posts.Directory != "" {
|
2019-05-28 17:44:50 +00:00
|
|
|
err = api.WritePost(cfg.Posts.Directory, p)
|
2018-09-19 00:38:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2018-09-16 00:29:15 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 00:18:45 +00:00
|
|
|
func CmdDelete(c *cli.Context) error {
|
2016-03-16 22:04:37 +00:00
|
|
|
friendlyID := c.Args().Get(0)
|
2016-02-23 16:02:48 +00:00
|
|
|
token := c.Args().Get(1)
|
2016-03-16 22:04:37 +00:00
|
|
|
if friendlyID == "" {
|
2017-04-13 04:02:37 +00:00
|
|
|
return cli.NewExitError("usage: writeas delete <postId> [<token>]", 1)
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-05-29 00:05:36 +00:00
|
|
|
u, _ := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
|
2016-02-23 16:02:48 +00:00
|
|
|
if token == "" {
|
|
|
|
// Search for the token locally
|
2019-05-29 00:05:36 +00:00
|
|
|
token = api.TokenFromID(c, friendlyID)
|
2018-09-19 00:54:07 +00:00
|
|
|
if token == "" && u == nil {
|
2019-05-28 17:44:50 +00:00
|
|
|
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)
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-28 17:44:50 +00:00
|
|
|
tor := config.IsTor(c)
|
2016-02-23 16:02:48 +00:00
|
|
|
if c.Int("tor-port") != 0 {
|
2019-05-28 17:44:50 +00:00
|
|
|
api.TorPort = c.Int("tor-port")
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
if tor {
|
2019-05-28 17:44:50 +00:00
|
|
|
log.Info(c, "Deleting via hidden service...")
|
2016-02-23 16:02:48 +00:00
|
|
|
} else {
|
2019-05-28 17:44:50 +00:00
|
|
|
log.Info(c, "Deleting...")
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 17:44:50 +00:00
|
|
|
err := api.DoDelete(c, friendlyID, token, tor)
|
2018-09-19 00:55:00 +00:00
|
|
|
if err != nil {
|
2019-06-05 20:37:16 +00:00
|
|
|
return cli.NewExitError(fmt.Sprintf("Couldn't delete remote copy: %v", err), 1)
|
2018-09-19 00:55:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Delete local file, if necessary
|
2019-05-29 00:05:36 +00:00
|
|
|
cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
|
2018-09-19 00:55:00 +00:00
|
|
|
if cfg.Posts.Directory != "" {
|
|
|
|
// TODO: handle deleting blog posts
|
2019-05-28 17:44:50 +00:00
|
|
|
err = fileutils.DeleteFile(filepath.Join(cfg.Posts.Directory, friendlyID+api.PostFileExt))
|
2018-09-19 00:55:00 +00:00
|
|
|
if err != nil {
|
2019-06-05 20:37:16 +00:00
|
|
|
return cli.NewExitError(fmt.Sprintf("Couldn't delete local copy: %v", err), 1)
|
2018-09-19 00:55:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 00:18:45 +00:00
|
|
|
func CmdUpdate(c *cli.Context) error {
|
2016-03-16 22:04:37 +00:00
|
|
|
friendlyID := c.Args().Get(0)
|
2016-02-23 16:02:48 +00:00
|
|
|
token := c.Args().Get(1)
|
2016-03-16 22:04:37 +00:00
|
|
|
if friendlyID == "" {
|
2017-04-13 04:02:37 +00:00
|
|
|
return cli.NewExitError("usage: writeas update <postId> [<token>]", 1)
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-05-29 00:05:36 +00:00
|
|
|
u, _ := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
|
2016-02-23 16:02:48 +00:00
|
|
|
if token == "" {
|
|
|
|
// Search for the token locally
|
2019-05-29 00:05:36 +00:00
|
|
|
token = api.TokenFromID(c, friendlyID)
|
2018-09-19 01:03:28 +00:00
|
|
|
if token == "" && u == nil {
|
2019-05-28 17:44:50 +00:00
|
|
|
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)
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read post body
|
2019-05-28 17:44:50 +00:00
|
|
|
fullPost := api.ReadStdIn()
|
2016-02-23 16:02:48 +00:00
|
|
|
|
2019-05-28 17:44:50 +00:00
|
|
|
tor := config.IsTor(c)
|
2016-02-23 16:02:48 +00:00
|
|
|
if c.Int("tor-port") != 0 {
|
2019-05-28 17:44:50 +00:00
|
|
|
api.TorPort = c.Int("tor-port")
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
if tor {
|
2019-05-28 17:44:50 +00:00
|
|
|
log.Info(c, "Updating via hidden service...")
|
2016-02-23 16:02:48 +00:00
|
|
|
} else {
|
2019-05-28 17:44:50 +00:00
|
|
|
log.Info(c, "Updating...")
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 17:44:50 +00:00
|
|
|
return api.DoUpdate(c, fullPost, friendlyID, token, c.String("font"), tor, c.Bool("code"))
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 00:18:45 +00:00
|
|
|
func CmdGet(c *cli.Context) error {
|
2016-03-16 22:04:37 +00:00
|
|
|
friendlyID := c.Args().Get(0)
|
|
|
|
if friendlyID == "" {
|
2017-04-13 04:02:37 +00:00
|
|
|
return cli.NewExitError("usage: writeas get <postId>", 1)
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 17:44:50 +00:00
|
|
|
tor := config.IsTor(c)
|
2016-02-23 16:02:48 +00:00
|
|
|
if c.Int("tor-port") != 0 {
|
2019-05-28 17:44:50 +00:00
|
|
|
api.TorPort = c.Int("tor-port")
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
if tor {
|
2019-05-28 17:44:50 +00:00
|
|
|
log.Info(c, "Getting via hidden service...")
|
2016-02-23 16:02:48 +00:00
|
|
|
} else {
|
2019-05-28 17:44:50 +00:00
|
|
|
log.Info(c, "Getting...")
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 17:44:50 +00:00
|
|
|
return api.DoFetch(friendlyID, config.UserAgent(c), tor)
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 00:18:45 +00:00
|
|
|
func CmdAdd(c *cli.Context) error {
|
2016-03-16 22:04:37 +00:00
|
|
|
friendlyID := c.Args().Get(0)
|
2016-02-23 16:02:48 +00:00
|
|
|
token := c.Args().Get(1)
|
2016-03-16 22:04:37 +00:00
|
|
|
if friendlyID == "" || token == "" {
|
2017-04-13 04:02:37 +00:00
|
|
|
return cli.NewExitError("usage: writeas add <postId> <token>", 1)
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-05-29 00:05:36 +00:00
|
|
|
err := api.AddPost(c, friendlyID, token)
|
2017-04-13 04:02:37 +00:00
|
|
|
return err
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-05-30 20:37:07 +00:00
|
|
|
func CmdListPosts(c *cli.Context) error {
|
2016-02-23 16:02:48 +00:00
|
|
|
urls := c.Bool("url")
|
|
|
|
ids := c.Bool("id")
|
|
|
|
|
2019-05-28 17:44:50 +00:00
|
|
|
var p api.Post
|
2019-05-29 00:05:36 +00:00
|
|
|
posts := api.GetPosts(c)
|
2019-06-07 14:30:13 +00:00
|
|
|
tw := tabwriter.NewWriter(os.Stdout, 10, 0, 2, ' ', tabwriter.TabIndent)
|
2019-05-30 22:57:55 +00:00
|
|
|
numPosts := len(*posts)
|
|
|
|
if ids || !urls && numPosts != 0 {
|
2019-06-05 22:32:11 +00:00
|
|
|
fmt.Fprintf(tw, "Local\t%s\t%s\t\n", "ID", "Token")
|
2019-05-30 22:57:55 +00:00
|
|
|
} else if numPosts != 0 {
|
2019-06-05 22:32:11 +00:00
|
|
|
fmt.Fprintf(tw, "Local\t%s\t%s\t\n", "URL", "Token")
|
2019-05-30 22:57:55 +00:00
|
|
|
} else {
|
|
|
|
fmt.Fprintf(tw, "No local posts found\n")
|
2019-05-30 20:37:07 +00:00
|
|
|
}
|
2016-02-23 16:02:48 +00:00
|
|
|
for i := range *posts {
|
2019-05-30 22:57:55 +00:00
|
|
|
p = (*posts)[numPosts-1-i]
|
2016-02-23 16:02:48 +00:00
|
|
|
if ids || !urls {
|
2019-06-05 22:32:11 +00:00
|
|
|
fmt.Fprintf(tw, "unsynced\t%s\t%s\t\n", p.ID, p.EditToken)
|
2019-05-30 20:37:07 +00:00
|
|
|
} else {
|
2019-06-05 22:32:11 +00:00
|
|
|
fmt.Fprintf(tw, "unsynced\t%s\t%s\t\n", getPostURL(c, p.ID), p.EditToken)
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
2019-05-30 20:37:07 +00:00
|
|
|
}
|
|
|
|
u, _ := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
|
|
|
|
if u != nil {
|
|
|
|
remotePosts, err := api.GetUserPosts(c)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(remotePosts) > 0 {
|
|
|
|
identifier := "URL"
|
|
|
|
if ids || !urls {
|
|
|
|
identifier = "ID"
|
2018-09-19 14:56:17 +00:00
|
|
|
}
|
2019-06-05 22:32:11 +00:00
|
|
|
fmt.Fprintf(tw, "\nAccount\t%s\t%s\t\n", identifier, "Title")
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
2019-05-30 20:37:07 +00:00
|
|
|
for _, p := range remotePosts {
|
|
|
|
identifier := getPostURL(c, p.ID)
|
|
|
|
if ids || !urls {
|
|
|
|
identifier = p.ID
|
2018-09-19 14:56:17 +00:00
|
|
|
}
|
2019-06-05 22:32:11 +00:00
|
|
|
synced := "unsynced"
|
|
|
|
if p.Synced {
|
|
|
|
synced = "synced"
|
2018-09-29 16:13:59 +00:00
|
|
|
}
|
2019-06-05 22:32:11 +00:00
|
|
|
fmt.Fprintf(tw, "%s\t%s\t%s\t\n", synced, identifier, p.Title)
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-30 20:37:07 +00:00
|
|
|
return tw.Flush()
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2016-02-23 16:02:48 +00:00
|
|
|
}
|
2018-09-08 20:00:48 +00:00
|
|
|
|
2019-06-07 16:44:29 +00:00
|
|
|
func CmdCollections(c *cli.Context) error {
|
|
|
|
u, err := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
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")
|
2019-06-10 14:43:08 +00:00
|
|
|
tw := tabwriter.NewWriter(os.Stdout, 8, 0, 2, ' ', tabwriter.TabIndent)
|
2019-06-07 16:44:29 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-06-10 22:01:34 +00:00
|
|
|
func CmdClaim(c *cli.Context) error {
|
|
|
|
u, err := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
localPosts := api.GetPosts(c)
|
|
|
|
if len(*localPosts) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
results, err := api.ClaimPosts(c, localPosts)
|
|
|
|
if err != nil {
|
|
|
|
return cli.NewExitError(fmt.Sprintf("Failed to claim posts: %v", err), 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, r := range *results {
|
|
|
|
fmt.Printf("Adding %s to user %s..", r.Post.ID, u.User.Username)
|
|
|
|
if r.ErrorMessage != "" {
|
|
|
|
fmt.Printf(" Failed\n")
|
|
|
|
if config.Debug() {
|
|
|
|
log.Errorln("Failed claiming post %s: %v", r.ID, r.ErrorMessage)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fmt.Printf(" OK\n")
|
|
|
|
// only delete local if successful
|
|
|
|
api.RemovePost(c.App.ExtraInfo()["configDir"], r.Post.ID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-05-28 00:18:45 +00:00
|
|
|
func CmdAuth(c *cli.Context) error {
|
2018-09-08 20:17:05 +00:00
|
|
|
// Check configuration
|
2019-05-29 00:05:36 +00:00
|
|
|
u, err := config.LoadUser(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
|
2018-09-08 20:17:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return cli.NewExitError(fmt.Sprintf("couldn't load config: %v", err), 1)
|
|
|
|
}
|
2018-09-10 16:33:05 +00:00
|
|
|
if u != nil && u.AccessToken != "" {
|
2018-09-28 01:17:07 +00:00
|
|
|
return cli.NewExitError("You're already authenticated as "+u.User.Username+". Log out with: writeas logout", 1)
|
2018-09-08 20:17:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Validate arguments and get password
|
2018-09-24 18:42:34 +00:00
|
|
|
username := c.Args().Get(0)
|
2018-09-08 20:00:48 +00:00
|
|
|
if username == "" {
|
2018-09-24 18:42:34 +00:00
|
|
|
return cli.NewExitError("usage: writeas auth <username>", 1)
|
2018-09-08 20:00:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Print("Password: ")
|
|
|
|
pass, err := gopass.GetPasswdMasked()
|
|
|
|
if err != nil {
|
|
|
|
return cli.NewExitError(fmt.Sprintf("error reading password: %v", err), 1)
|
|
|
|
}
|
2018-09-08 20:17:05 +00:00
|
|
|
|
2018-09-08 20:00:48 +00:00
|
|
|
// Validate password
|
|
|
|
if len(pass) == 0 {
|
|
|
|
return cli.NewExitError("Please enter your password.", 1)
|
|
|
|
}
|
2019-05-28 17:44:50 +00:00
|
|
|
err = api.DoLogIn(c, username, string(pass))
|
2019-05-29 18:20:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return cli.NewExitError(fmt.Sprintf("error logging in: %v", err), 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2018-09-08 20:00:48 +00:00
|
|
|
}
|
2018-09-12 15:26:20 +00:00
|
|
|
|
2019-05-28 00:18:45 +00:00
|
|
|
func CmdLogOut(c *cli.Context) error {
|
2019-05-28 17:44:50 +00:00
|
|
|
return api.DoLogOut(c)
|
2018-09-12 15:26:20 +00:00
|
|
|
}
|