A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
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.
 
 
 
 
 

187 lines
9.3 KiB

  1. /*
  2. * Copyright © 2018 A Bunch Tell LLC.
  3. *
  4. * This file is part of WriteFreely.
  5. *
  6. * WriteFreely is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License, included
  8. * in the LICENSE file in this source code package.
  9. */
  10. package writefreely
  11. import (
  12. "github.com/gorilla/mux"
  13. "github.com/writeas/go-webfinger"
  14. "github.com/writeas/web-core/log"
  15. "github.com/writeas/writefreely/config"
  16. "github.com/writefreely/go-nodeinfo"
  17. "net/http"
  18. "strings"
  19. )
  20. func initRoutes(handler *Handler, r *mux.Router, cfg *config.Config, db *datastore) {
  21. hostSubroute := cfg.App.Host[strings.Index(cfg.App.Host, "://")+3:]
  22. if cfg.App.SingleUser {
  23. hostSubroute = "{domain}"
  24. } else {
  25. if strings.HasPrefix(hostSubroute, "localhost") {
  26. hostSubroute = "localhost"
  27. }
  28. }
  29. if cfg.App.SingleUser {
  30. log.Info("Adding %s routes (single user)...", hostSubroute)
  31. } else {
  32. log.Info("Adding %s routes (multi-user)...", hostSubroute)
  33. }
  34. // Primary app routes
  35. write := r.PathPrefix("/").Subrouter()
  36. // Federation endpoint configurations
  37. wf := webfinger.Default(wfResolver{db, cfg})
  38. wf.NoTLSHandler = nil
  39. // Federation endpoints
  40. // host-meta
  41. write.HandleFunc("/.well-known/host-meta", handler.Web(handleViewHostMeta, UserLevelOptional))
  42. // webfinger
  43. write.HandleFunc(webfinger.WebFingerPath, handler.LogHandlerFunc(http.HandlerFunc(wf.Webfinger)))
  44. // nodeinfo
  45. niCfg := nodeInfoConfig(db, cfg)
  46. ni := nodeinfo.NewService(*niCfg, nodeInfoResolver{cfg, db})
  47. write.HandleFunc(nodeinfo.NodeInfoPath, handler.LogHandlerFunc(http.HandlerFunc(ni.NodeInfoDiscover)))
  48. write.HandleFunc(niCfg.InfoURL, handler.LogHandlerFunc(http.HandlerFunc(ni.NodeInfo)))
  49. // Set up dyamic page handlers
  50. // Handle auth
  51. auth := write.PathPrefix("/api/auth/").Subrouter()
  52. if cfg.App.OpenRegistration {
  53. auth.HandleFunc("/signup", handler.All(apiSignup)).Methods("POST")
  54. }
  55. auth.HandleFunc("/login", handler.All(login)).Methods("POST")
  56. auth.HandleFunc("/read", handler.WebErrors(handleWebCollectionUnlock, UserLevelNone)).Methods("POST")
  57. auth.HandleFunc("/me", handler.All(handleAPILogout)).Methods("DELETE")
  58. // Handle logged in user sections
  59. me := write.PathPrefix("/me").Subrouter()
  60. me.HandleFunc("/", handler.Redirect("/me", UserLevelUser))
  61. me.HandleFunc("/c", handler.Redirect("/me/c/", UserLevelUser)).Methods("GET")
  62. me.HandleFunc("/c/", handler.User(viewCollections)).Methods("GET")
  63. me.HandleFunc("/c/{collection}", handler.User(viewEditCollection)).Methods("GET")
  64. me.HandleFunc("/c/{collection}/stats", handler.User(viewStats)).Methods("GET")
  65. me.HandleFunc("/posts", handler.Redirect("/me/posts/", UserLevelUser)).Methods("GET")
  66. me.HandleFunc("/posts/", handler.User(viewArticles)).Methods("GET")
  67. me.HandleFunc("/posts/export.csv", handler.Download(viewExportPosts, UserLevelUser)).Methods("GET")
  68. me.HandleFunc("/posts/export.zip", handler.Download(viewExportPosts, UserLevelUser)).Methods("GET")
  69. me.HandleFunc("/posts/export.json", handler.Download(viewExportPosts, UserLevelUser)).Methods("GET")
  70. me.HandleFunc("/export", handler.User(viewExportOptions)).Methods("GET")
  71. me.HandleFunc("/export.json", handler.Download(viewExportFull, UserLevelUser)).Methods("GET")
  72. me.HandleFunc("/settings", handler.User(viewSettings)).Methods("GET")
  73. me.HandleFunc("/logout", handler.Web(viewLogout, UserLevelNone)).Methods("GET")
  74. write.HandleFunc("/api/me", handler.All(viewMeAPI)).Methods("GET")
  75. apiMe := write.PathPrefix("/api/me/").Subrouter()
  76. apiMe.HandleFunc("/", handler.All(viewMeAPI)).Methods("GET")
  77. apiMe.HandleFunc("/posts", handler.UserAPI(viewMyPostsAPI)).Methods("GET")
  78. apiMe.HandleFunc("/collections", handler.UserAPI(viewMyCollectionsAPI)).Methods("GET")
  79. apiMe.HandleFunc("/password", handler.All(updatePassphrase)).Methods("POST")
  80. apiMe.HandleFunc("/self", handler.All(updateSettings)).Methods("POST")
  81. // Sign up validation
  82. write.HandleFunc("/api/alias", handler.All(handleUsernameCheck)).Methods("POST")
  83. // Handle collections
  84. write.HandleFunc("/api/collections", handler.All(newCollection)).Methods("POST")
  85. apiColls := write.PathPrefix("/api/collections/").Subrouter()
  86. apiColls.HandleFunc("/{alias:[0-9a-zA-Z\\-]+}", handler.All(fetchCollection)).Methods("GET")
  87. apiColls.HandleFunc("/{alias:[0-9a-zA-Z\\-]+}", handler.All(existingCollection)).Methods("POST", "DELETE")
  88. apiColls.HandleFunc("/{alias}/posts", handler.All(fetchCollectionPosts)).Methods("GET")
  89. apiColls.HandleFunc("/{alias}/posts", handler.All(newPost)).Methods("POST")
  90. apiColls.HandleFunc("/{alias}/posts/{post}", handler.All(fetchPost)).Methods("GET")
  91. apiColls.HandleFunc("/{alias}/posts/{post:[a-zA-Z0-9]{10}}", handler.All(existingPost)).Methods("POST")
  92. apiColls.HandleFunc("/{alias}/posts/{post}/{property}", handler.All(fetchPostProperty)).Methods("GET")
  93. apiColls.HandleFunc("/{alias}/collect", handler.All(addPost)).Methods("POST")
  94. apiColls.HandleFunc("/{alias}/pin", handler.All(pinPost)).Methods("POST")
  95. apiColls.HandleFunc("/{alias}/unpin", handler.All(pinPost)).Methods("POST")
  96. apiColls.HandleFunc("/{alias}/inbox", handler.All(handleFetchCollectionInbox)).Methods("POST")
  97. apiColls.HandleFunc("/{alias}/outbox", handler.All(handleFetchCollectionOutbox)).Methods("GET")
  98. apiColls.HandleFunc("/{alias}/following", handler.All(handleFetchCollectionFollowing)).Methods("GET")
  99. apiColls.HandleFunc("/{alias}/followers", handler.All(handleFetchCollectionFollowers)).Methods("GET")
  100. // Handle posts
  101. write.HandleFunc("/api/posts", handler.All(newPost)).Methods("POST")
  102. posts := write.PathPrefix("/api/posts/").Subrouter()
  103. posts.HandleFunc("/{post:[a-zA-Z0-9]{10}}", handler.All(fetchPost)).Methods("GET")
  104. posts.HandleFunc("/{post:[a-zA-Z0-9]{10}}", handler.All(existingPost)).Methods("POST", "PUT")
  105. posts.HandleFunc("/{post:[a-zA-Z0-9]{10}}", handler.All(deletePost)).Methods("DELETE")
  106. posts.HandleFunc("/{post:[a-zA-Z0-9]{10}}/{property}", handler.All(fetchPostProperty)).Methods("GET")
  107. posts.HandleFunc("/claim", handler.All(addPost)).Methods("POST")
  108. posts.HandleFunc("/disperse", handler.All(dispersePost)).Methods("POST")
  109. if cfg.App.OpenRegistration {
  110. write.HandleFunc("/auth/signup", handler.Web(handleWebSignup, UserLevelNoneRequired)).Methods("POST")
  111. }
  112. write.HandleFunc("/auth/login", handler.Web(webLogin, UserLevelNoneRequired)).Methods("POST")
  113. write.HandleFunc("/admin", handler.Admin(handleViewAdminDash)).Methods("GET")
  114. write.HandleFunc("/admin/update/config", handler.Admin(handleAdminUpdateConfig)).Methods("POST")
  115. write.HandleFunc("/admin/update/{page}", handler.Admin(handleAdminUpdateSite)).Methods("POST")
  116. // Handle special pages first
  117. write.HandleFunc("/login", handler.Web(viewLogin, UserLevelNoneRequired))
  118. // TODO: show a reader-specific 404 page if the function is disabled
  119. // TODO: change this based on configuration for either public or private-to-this-instance
  120. readPerm := UserLevelOptional
  121. write.HandleFunc("/read", handler.Web(viewLocalTimeline, readPerm))
  122. RouteRead(handler, readPerm, write.PathPrefix("/read").Subrouter())
  123. draftEditPrefix := ""
  124. if cfg.App.SingleUser {
  125. draftEditPrefix = "/d"
  126. write.HandleFunc("/me/new", handler.Web(handleViewPad, UserLevelOptional)).Methods("GET")
  127. } else {
  128. write.HandleFunc("/new", handler.Web(handleViewPad, UserLevelOptional)).Methods("GET")
  129. }
  130. // All the existing stuff
  131. write.HandleFunc(draftEditPrefix+"/{action}/edit", handler.Web(handleViewPad, UserLevelOptional)).Methods("GET")
  132. write.HandleFunc(draftEditPrefix+"/{action}/meta", handler.Web(handleViewMeta, UserLevelOptional)).Methods("GET")
  133. // Collections
  134. if cfg.App.SingleUser {
  135. RouteCollections(handler, write.PathPrefix("/").Subrouter())
  136. } else {
  137. write.HandleFunc("/{prefix:[@~$!\\-+]}{collection}", handler.Web(handleViewCollection, UserLevelOptional))
  138. write.HandleFunc("/{collection}/", handler.Web(handleViewCollection, UserLevelOptional))
  139. RouteCollections(handler, write.PathPrefix("/{prefix:[@~$!\\-+]?}{collection}").Subrouter())
  140. // Posts
  141. }
  142. write.HandleFunc(draftEditPrefix+"/{post}", handler.Web(handleViewPost, UserLevelOptional))
  143. write.HandleFunc("/", handler.Web(handleViewHome, UserLevelOptional))
  144. }
  145. func RouteCollections(handler *Handler, r *mux.Router) {
  146. r.HandleFunc("/page/{page:[0-9]+}", handler.Web(handleViewCollection, UserLevelOptional))
  147. r.HandleFunc("/tag:{tag}", handler.Web(handleViewCollectionTag, UserLevelOptional))
  148. r.HandleFunc("/tag:{tag}/feed/", handler.Web(ViewFeed, UserLevelOptional))
  149. r.HandleFunc("/tags/{tag}", handler.Web(handleViewCollectionTag, UserLevelOptional))
  150. r.HandleFunc("/sitemap.xml", handler.All(handleViewSitemap))
  151. r.HandleFunc("/feed/", handler.All(ViewFeed))
  152. r.HandleFunc("/{slug}", handler.Web(viewCollectionPost, UserLevelOptional))
  153. r.HandleFunc("/{slug}/edit", handler.Web(handleViewPad, UserLevelUser))
  154. r.HandleFunc("/{slug}/edit/meta", handler.Web(handleViewMeta, UserLevelUser))
  155. r.HandleFunc("/{slug}/", handler.Web(handleCollectionPostRedirect, UserLevelOptional)).Methods("GET")
  156. }
  157. func RouteRead(handler *Handler, readPerm UserLevel, r *mux.Router) {
  158. r.HandleFunc("/api/posts", handler.Web(viewLocalTimelineAPI, readPerm))
  159. r.HandleFunc("/p/{page}", handler.Web(viewLocalTimeline, readPerm))
  160. r.HandleFunc("/feed/", handler.Web(viewLocalTimelineFeed, readPerm))
  161. r.HandleFunc("/t/{tag}", handler.Web(viewLocalTimeline, readPerm))
  162. r.HandleFunc("/a/{post}", handler.Web(handlePostIDRedirect, readPerm))
  163. r.HandleFunc("/{author}", handler.Web(viewLocalTimeline, readPerm))
  164. r.HandleFunc("/", handler.Web(viewLocalTimeline, readPerm))
  165. }