1
0
mirror of https://github.com/writeas/writeas-cli synced 2025-07-26 23:08:16 +00:00
writeas-cli/commands/commands.go

452 lines
11 KiB
Go
Raw Normal View History

package commands
2016-02-23 16:02:48 +00:00
import (
"fmt"
"io/ioutil"
2016-02-23 16:02:48 +00:00
"os"
"strings"
"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"
2016-02-23 16:02:48 +00:00
)
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 {
2019-06-21 02:55:30 +00:00
return cli.NewExitError(err.Error(), 1)
}
return nil
2016-02-23 16:02:48 +00:00
}
func CmdNew(c *cli.Context) error {
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)
}
log.InfolnQuit("Empty post. Bye!")
2016-02-23 16:02:48 +00:00
}
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"))
2016-02-23 16:02:48 +00:00
if err != nil {
log.Errorln("Error posting: %s\n%s", err, config.MessageRetryCompose(fname))
return cli.NewExitError("", 1)
2016-02-23 16:02:48 +00:00
}
// Clean up temporary post
if fname != "" {
os.Remove(fname)
}
return nil
2016-02-23 16:02:48 +00:00
}
func CmdPublish(c *cli.Context) error {
filename := c.Args().Get(0)
if filename == "" {
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"))
2019-06-21 02:55:30 +00:00
if err != nil {
return cli.NewExitError(err.Error(), 1)
}
// TODO: write local file if directory is set
2019-06-21 02:55:30 +00:00
return nil
}
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 == "" {
return cli.NewExitError("usage: "+executable.Name()+" delete <postId> [<token>]", 1)
2016-02-23 16:02:48 +00:00
}
u, _ := config.LoadUser(c)
2016-02-23 16:02:48 +00:00
if token == "" {
// Search for the token locally
token = api.TokenFromID(c, friendlyID)
2018-09-19 00:54:07 +00:00
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: "+executable.Name()+" delete %s <token>", friendlyID)
2016-02-23 16:02:48 +00:00
}
}
if config.IsTor(c) {
log.Info(c, "Deleting via hidden service...")
2016-02-23 16:02:48 +00:00
} else {
log.Info(c, "Deleting...")
2016-02-23 16:02:48 +00:00
}
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
2016-02-23 16:02:48 +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 == "" {
return cli.NewExitError("usage: "+executable.Name()+" update <postId> [<token>]", 1)
2016-02-23 16:02:48 +00:00
}
u, _ := config.LoadUser(c)
2016-02-23 16:02:48 +00:00
if token == "" {
// Search for the token locally
token = api.TokenFromID(c, friendlyID)
2018-09-19 01:03:28 +00:00
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: "+executable.Name()+" update %s <token>", friendlyID)
2016-02-23 16:02:48 +00:00
}
}
// Read post body
fullPost := api.ReadStdIn()
2016-02-23 16:02:48 +00:00
if config.IsTor(c) {
log.Info(c, "Updating via hidden service...")
2016-02-23 16:02:48 +00:00
} else {
log.Info(c, "Updating...")
2016-02-23 16:02:48 +00:00
}
2019-06-21 02:55:30 +00:00
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
2016-02-23 16:02:48 +00:00
}
func CmdGet(c *cli.Context) error {
2016-03-16 22:04:37 +00:00
friendlyID := c.Args().Get(0)
if friendlyID == "" {
return cli.NewExitError("usage: "+executable.Name()+" get <postId>", 1)
2016-02-23 16:02:48 +00:00
}
if config.IsTor(c) {
log.Info(c, "Getting via hidden service...")
2016-02-23 16:02:48 +00:00
} else {
log.Info(c, "Getting...")
2016-02-23 16:02:48 +00:00
}
2019-06-21 02:55:30 +00:00
err := api.DoFetch(c, friendlyID)
if err != nil {
return cli.NewExitError(fmt.Sprintf("%v", err), 1)
}
return nil
2016-02-23 16:02:48 +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 == "" {
return cli.NewExitError("usage: "+executable.Name()+" add <postId> <token>", 1)
2016-02-23 16:02:48 +00:00
}
err := api.AddPost(c, friendlyID, token)
2019-06-21 02:55:30 +00:00
if err != nil {
return cli.NewExitError(fmt.Sprintf("%v", err), 1)
}
return nil
2016-02-23 16:02:48 +00:00
}
func CmdListPosts(c *cli.Context) error {
2016-02-23 16:02:48 +00:00
urls := c.Bool("url")
ids := c.Bool("id")
details := c.Bool("v")
2016-02-23 16:02:48 +00:00
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 {
if c.App.Name == "wf" {
fmt.Println("Draft Posts")
} else {
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")
2016-02-23 16:02:48 +00:00
}
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)
}
2016-02-23 16:02:48 +00:00
}
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))
2016-02-23 16:02:48 +00:00
}
}
return nil
}
func getPostURL(c *cli.Context, slug string) string {
var base string
if c.App.Name == "writeas" {
if config.IsDev() {
base = config.DevBaseURL
} else {
base = config.WriteasBaseURL
}
} else {
if host := api.HostURL(c); host != "" {
base = host
} else {
// TODO handle error, or load config globally, see T601
// https://phabricator.write.as/T601
cfg, _ := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
if cfg.Default.Host != "" && cfg.Default.User != "" {
if parts := strings.Split(cfg.Default.Host, "://"); len(parts) > 1 {
base = cfg.Default.Host
} else {
base = "https://" + cfg.Default.Host
}
}
}
}
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
}
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: "+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: "+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 {
username := c.Args().Get(0)
if username == "" && c.GlobalIsSet("user") {
username = c.GlobalString("user")
}
// 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 != "" && username == u.User.Username {
return cli.NewExitError("You're already authenticated as "+u.User.Username, 1)
}
// Validate arguments and get password
if username == "" {
cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
if err != nil {
return cli.NewExitError(fmt.Sprintf("Failed to load config: %v", err), 1)
}
if cfg.Default.Host != "" && cfg.Default.User != "" {
username = cfg.Default.User
fmt.Printf("No user provided, using default user %s for host %s...\n", cfg.Default.User, cfg.Default.Host)
} else {
return cli.NewExitError("usage: "+executable.Name()+" auth <username>", 1)
}
}
// Take password from argument, and fall back to input
pass := c.String("p")
if pass == "" {
fmt.Print("Password: ")
enteredPass, err := gopass.GetPasswdMasked()
if err != nil {
return cli.NewExitError(fmt.Sprintf("error reading password: %v", err), 1)
}
// Validate password
if len(enteredPass) == 0 {
return cli.NewExitError("Please enter your password.", 1)
}
pass = string(enteredPass)
}
if config.IsTor(c) {
log.Info(c, "Logging in via hidden service...")
} else {
log.Info(c, "Logging in...")
}
err = api.DoLogIn(c, username, 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-12 15:26:20 +00:00
func CmdLogOut(c *cli.Context) error {
if config.IsTor(c) {
log.Info(c, "Logging out via hidden service...")
} else {
log.Info(c, "Logging out...")
}
2019-06-21 02:55:30 +00:00
err := api.DoLogOut(c)
if err != nil {
return cli.NewExitError(fmt.Sprintf("error logging out: %v", err), 1)
}
return nil
2018-09-12 15:26:20 +00:00
}