Command line client for Write.as https://write.as/apps/cli
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

298 lines
6.9 KiB

  1. package api
  2. import (
  3. "fmt"
  4. "github.com/atotto/clipboard"
  5. writeas "github.com/writeas/go-writeas/v2"
  6. "github.com/writeas/web-core/posts"
  7. "github.com/writeas/writeas-cli/config"
  8. "github.com/writeas/writeas-cli/executable"
  9. "github.com/writeas/writeas-cli/log"
  10. cli "gopkg.in/urfave/cli.v1"
  11. )
  12. func HostURL(c *cli.Context) string {
  13. host := c.GlobalString("host")
  14. if host == "" {
  15. return ""
  16. }
  17. insecure := c.Bool("insecure")
  18. scheme := "https://"
  19. if insecure {
  20. scheme = "http://"
  21. }
  22. return scheme + host
  23. }
  24. func newClient(c *cli.Context) (*writeas.Client, error) {
  25. var client *writeas.Client
  26. var clientConfig writeas.Config
  27. cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
  28. if err != nil {
  29. return nil, fmt.Errorf("Failed to load configuration file: %v", err)
  30. }
  31. if host := HostURL(c); host != "" {
  32. clientConfig.URL = host + "/api"
  33. } else if cfg.Default.Host != "" && cfg.Default.User != "" {
  34. clientConfig.URL = "https://" + cfg.Default.Host + "/api"
  35. } else if config.IsDev() {
  36. clientConfig.URL = config.DevBaseURL + "/api"
  37. } else if c.App.Name == "writeas" {
  38. clientConfig.URL = config.WriteasBaseURL + "/api"
  39. } else {
  40. return nil, fmt.Errorf("Must supply a host. Example: %s --host example.com %s", executable.Name(), c.Command.Name)
  41. }
  42. if config.IsTor(c) {
  43. clientConfig.URL = config.TorURL(c)
  44. clientConfig.TorPort = config.TorPort(c)
  45. }
  46. client = writeas.NewClientWith(clientConfig)
  47. client.UserAgent = config.UserAgent(c)
  48. return client, nil
  49. }
  50. // DoFetch retrieves the Write.as post with the given friendlyID,
  51. // optionally via the Tor hidden service.
  52. func DoFetch(c *cli.Context, friendlyID string) error {
  53. cl, err := newClient(c)
  54. if err != nil {
  55. return err
  56. }
  57. p, err := cl.GetPost(friendlyID)
  58. if err != nil {
  59. return err
  60. }
  61. if p.Title != "" {
  62. fmt.Printf("# %s\n\n", string(p.Title))
  63. }
  64. fmt.Printf("%s\n", string(p.Content))
  65. return nil
  66. }
  67. // DoFetchPosts retrieves all remote posts for the
  68. // authenticated user
  69. func DoFetchPosts(c *cli.Context) ([]writeas.Post, error) {
  70. cl, err := newClient(c)
  71. if err != nil {
  72. return nil, fmt.Errorf("%v", err)
  73. }
  74. u, _ := config.LoadUser(c)
  75. if u != nil {
  76. cl.SetToken(u.AccessToken)
  77. } else {
  78. return nil, fmt.Errorf("Not currently logged in. Authenticate with: " + executable.Name() + " auth <username>")
  79. }
  80. posts, err := cl.GetUserPosts()
  81. if err != nil {
  82. return nil, err
  83. }
  84. return *posts, nil
  85. }
  86. // DoPost creates a Write.as post, returning an error if it was
  87. // unsuccessful.
  88. func DoPost(c *cli.Context, post []byte, font string, encrypt, code bool) (*writeas.Post, error) {
  89. cl, err := newClient(c)
  90. if err != nil {
  91. return nil, fmt.Errorf("%v", err)
  92. }
  93. pp := &writeas.PostParams{
  94. Font: config.GetFont(code, font),
  95. Collection: config.Collection(c),
  96. }
  97. pp.Title, pp.Content = posts.ExtractTitle(string(post))
  98. if lang := config.Language(c, true); lang != "" {
  99. pp.Language = &lang
  100. }
  101. p, err := cl.CreatePost(pp)
  102. if err != nil {
  103. return nil, fmt.Errorf("Unable to post: %v", err)
  104. }
  105. cfg, err := config.LoadConfig(config.UserDataDir(c.App.ExtraInfo()["configDir"]))
  106. if err != nil {
  107. return nil, fmt.Errorf("Couldn't check for config file: %v", err)
  108. }
  109. var url string
  110. if p.Collection != nil {
  111. url = p.Collection.URL + p.Slug
  112. } else {
  113. if host := HostURL(c); host != "" {
  114. url = host
  115. } else if cfg.Default.Host != "" {
  116. url = cfg.Default.Host
  117. } else if config.IsDev() {
  118. url = config.DevBaseURL
  119. } else if config.IsTor(c) {
  120. url = config.TorBaseURL
  121. } else {
  122. url = config.WriteasBaseURL
  123. }
  124. url += "/" + p.ID
  125. // Output URL in requested format
  126. if c.Bool("md") {
  127. url += ".md"
  128. }
  129. }
  130. if cl.Token() == "" {
  131. // Store post locally, since we're not authenticated
  132. AddPost(c, p.ID, p.Token)
  133. }
  134. // Copy URL to clipboard
  135. err = clipboard.WriteAll(string(url))
  136. if err != nil {
  137. log.Errorln(executable.Name()+": Didn't copy to clipboard: %s", err)
  138. } else {
  139. log.Info(c, "Copied to clipboard.")
  140. }
  141. // Output URL
  142. fmt.Printf("%s\n", url)
  143. return p, nil
  144. }
  145. // DoFetchCollections retrieves a list of the currently logged in users
  146. // collections.
  147. func DoFetchCollections(c *cli.Context) ([]RemoteColl, error) {
  148. cl, err := newClient(c)
  149. if err != nil {
  150. if config.Debug() {
  151. log.ErrorlnQuit("could not create client: %v", err)
  152. }
  153. return nil, fmt.Errorf("Couldn't create new client")
  154. }
  155. u, _ := config.LoadUser(c)
  156. if u != nil {
  157. cl.SetToken(u.AccessToken)
  158. } else {
  159. return nil, fmt.Errorf("Not currently logged in. Authenticate with: " + executable.Name() + " auth <username>")
  160. }
  161. colls, err := cl.GetUserCollections()
  162. if err != nil {
  163. if config.Debug() {
  164. log.ErrorlnQuit("failed fetching user collections: %v", err)
  165. }
  166. return nil, fmt.Errorf("Couldn't get user blogs")
  167. }
  168. out := make([]RemoteColl, len(*colls))
  169. for i, c := range *colls {
  170. coll := RemoteColl{
  171. Alias: c.Alias,
  172. Title: c.Title,
  173. URL: c.URL,
  174. }
  175. out[i] = coll
  176. }
  177. return out, nil
  178. }
  179. // DoUpdate updates the given post on Write.as.
  180. func DoUpdate(c *cli.Context, post []byte, friendlyID, token, font string, code bool) error {
  181. cl, err := newClient(c)
  182. if err != nil {
  183. return fmt.Errorf("%v", err)
  184. }
  185. params := writeas.PostParams{}
  186. params.Title, params.Content = posts.ExtractTitle(string(post))
  187. if lang := config.Language(c, false); lang != "" {
  188. params.Language = &lang
  189. }
  190. if code || font != "" {
  191. params.Font = config.GetFont(code, font)
  192. }
  193. _, err = cl.UpdatePost(friendlyID, token, &params)
  194. if err != nil {
  195. if config.Debug() {
  196. log.ErrorlnQuit("Problem updating: %v", err)
  197. }
  198. return fmt.Errorf("Post doesn't exist, or bad edit token given.")
  199. }
  200. return nil
  201. }
  202. // DoDelete deletes the given post on Write.as, and removes any local references
  203. func DoDelete(c *cli.Context, friendlyID, token string) error {
  204. cl, err := newClient(c)
  205. if err != nil {
  206. return fmt.Errorf("%v", err)
  207. }
  208. err = cl.DeletePost(friendlyID, token)
  209. if err != nil {
  210. if config.Debug() {
  211. log.ErrorlnQuit("Problem deleting: %v", err)
  212. }
  213. return fmt.Errorf("Post doesn't exist, or bad edit token given.")
  214. }
  215. RemovePost(c, friendlyID)
  216. return nil
  217. }
  218. func DoLogIn(c *cli.Context, username, password string) error {
  219. cl, err := newClient(c)
  220. if err != nil {
  221. return fmt.Errorf("%v", err)
  222. }
  223. u, err := cl.LogIn(username, password)
  224. if err != nil {
  225. if config.Debug() {
  226. log.ErrorlnQuit("Problem logging in: %v", err)
  227. }
  228. return err
  229. }
  230. err = config.SaveUser(c, u)
  231. if err != nil {
  232. return err
  233. }
  234. log.Info(c, "Logged in as %s.\n", u.User.Username)
  235. return nil
  236. }
  237. func DoLogOut(c *cli.Context) error {
  238. cl, err := newClient(c)
  239. if err != nil {
  240. return fmt.Errorf("%v", err)
  241. }
  242. u, _ := config.LoadUser(c)
  243. if u != nil {
  244. cl.SetToken(u.AccessToken)
  245. } else if c.App.Name == "writeas" {
  246. return fmt.Errorf("Not currently logged in. Authenticate with: " + executable.Name() + " auth <username>")
  247. }
  248. err = cl.LogOut()
  249. if err != nil {
  250. if config.Debug() {
  251. log.ErrorlnQuit("Problem logging out: %v", err)
  252. }
  253. return err
  254. }
  255. // delete local user file
  256. return config.DeleteUser(c)
  257. }