Command line client for Write.as https://write.as/apps/cli
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

331 Zeilen
7.7 KiB

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