2018-12-24 17:45:15 +00:00
|
|
|
/*
|
2019-06-13 22:22:18 +00:00
|
|
|
* Copyright © 2018-2019 A Bunch Tell LLC.
|
2018-12-24 17:45:15 +00:00
|
|
|
*
|
|
|
|
* This file is part of WriteFreely.
|
|
|
|
*
|
|
|
|
* WriteFreely is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Affero General Public License, included
|
|
|
|
* in the LICENSE file in this source code package.
|
|
|
|
*/
|
2018-12-31 06:05:26 +00:00
|
|
|
|
2018-10-15 18:44:15 +00:00
|
|
|
package writefreely
|
|
|
|
|
|
|
|
import (
|
2019-09-13 17:58:17 +00:00
|
|
|
"net/http"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
2018-10-15 18:44:15 +00:00
|
|
|
"github.com/gorilla/mux"
|
2018-11-08 06:28:08 +00:00
|
|
|
"github.com/writeas/go-webfinger"
|
2018-10-15 18:44:15 +00:00
|
|
|
"github.com/writeas/web-core/log"
|
2018-12-17 06:27:04 +00:00
|
|
|
"github.com/writefreely/go-nodeinfo"
|
2018-10-15 18:44:15 +00:00
|
|
|
)
|
|
|
|
|
2019-06-13 22:22:18 +00:00
|
|
|
// InitStaticRoutes adds routes for serving static files.
|
|
|
|
// TODO: this should just be a func, not method
|
|
|
|
func (app *App) InitStaticRoutes(r *mux.Router) {
|
|
|
|
// Handle static files
|
|
|
|
fs := http.FileServer(http.Dir(filepath.Join(app.cfg.Server.StaticParentDir, staticDir)))
|
|
|
|
app.shttp = http.NewServeMux()
|
|
|
|
app.shttp.Handle("/", fs)
|
|
|
|
r.PathPrefix("/").Handler(fs)
|
|
|
|
}
|
|
|
|
|
2019-06-13 22:50:23 +00:00
|
|
|
// InitRoutes adds dynamic routes for the given mux.Router.
|
2019-06-14 01:56:13 +00:00
|
|
|
func InitRoutes(apper Apper, r *mux.Router) *mux.Router {
|
2019-06-13 22:50:23 +00:00
|
|
|
// Create handler
|
2019-06-14 01:56:13 +00:00
|
|
|
handler := NewWFHandler(apper)
|
2019-06-13 22:50:23 +00:00
|
|
|
|
|
|
|
// Set up routes
|
2019-06-14 01:56:13 +00:00
|
|
|
hostSubroute := apper.App().cfg.App.Host[strings.Index(apper.App().cfg.App.Host, "://")+3:]
|
|
|
|
if apper.App().cfg.App.SingleUser {
|
2018-10-15 18:44:15 +00:00
|
|
|
hostSubroute = "{domain}"
|
|
|
|
} else {
|
|
|
|
if strings.HasPrefix(hostSubroute, "localhost") {
|
|
|
|
hostSubroute = "localhost"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 01:56:13 +00:00
|
|
|
if apper.App().cfg.App.SingleUser {
|
2018-10-15 18:44:15 +00:00
|
|
|
log.Info("Adding %s routes (single user)...", hostSubroute)
|
2018-11-08 04:43:11 +00:00
|
|
|
} else {
|
|
|
|
log.Info("Adding %s routes (multi-user)...", hostSubroute)
|
2018-10-15 18:44:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Primary app routes
|
2018-11-08 15:17:17 +00:00
|
|
|
write := r.PathPrefix("/").Subrouter()
|
2018-10-17 22:57:37 +00:00
|
|
|
|
2018-11-08 06:28:08 +00:00
|
|
|
// Federation endpoint configurations
|
2019-06-14 01:56:13 +00:00
|
|
|
wf := webfinger.Default(wfResolver{apper.App().db, apper.App().cfg})
|
2018-11-08 06:28:08 +00:00
|
|
|
wf.NoTLSHandler = nil
|
|
|
|
|
2018-10-17 22:57:37 +00:00
|
|
|
// Federation endpoints
|
2018-11-08 06:28:08 +00:00
|
|
|
// host-meta
|
2019-06-17 01:22:56 +00:00
|
|
|
write.HandleFunc("/.well-known/host-meta", handler.Web(handleViewHostMeta, UserLevelReader))
|
2018-11-08 06:28:08 +00:00
|
|
|
// webfinger
|
|
|
|
write.HandleFunc(webfinger.WebFingerPath, handler.LogHandlerFunc(http.HandlerFunc(wf.Webfinger)))
|
2018-10-17 22:57:37 +00:00
|
|
|
// nodeinfo
|
2019-06-14 01:56:13 +00:00
|
|
|
niCfg := nodeInfoConfig(apper.App().db, apper.App().cfg)
|
|
|
|
ni := nodeinfo.NewService(*niCfg, nodeInfoResolver{apper.App().cfg, apper.App().db})
|
2018-10-17 22:57:37 +00:00
|
|
|
write.HandleFunc(nodeinfo.NodeInfoPath, handler.LogHandlerFunc(http.HandlerFunc(ni.NodeInfoDiscover)))
|
|
|
|
write.HandleFunc(niCfg.InfoURL, handler.LogHandlerFunc(http.HandlerFunc(ni.NodeInfo)))
|
2018-11-08 04:43:11 +00:00
|
|
|
|
2019-10-11 07:05:18 +00:00
|
|
|
// handle mentions
|
2020-02-08 17:57:49 +00:00
|
|
|
write.HandleFunc("/@/{handle}", handler.Web(handleViewMention, UserLevelReader))
|
2019-10-11 07:05:18 +00:00
|
|
|
|
2020-01-02 21:19:26 +00:00
|
|
|
configureSlackOauth(handler, write, apper.App())
|
|
|
|
configureWriteAsOauth(handler, write, apper.App())
|
2020-03-11 10:51:12 +00:00
|
|
|
configureGitlabOauth(handler, write, apper.App())
|
2019-12-28 20:15:47 +00:00
|
|
|
|
2018-11-08 06:31:01 +00:00
|
|
|
// Set up dyamic page handlers
|
|
|
|
// Handle auth
|
|
|
|
auth := write.PathPrefix("/api/auth/").Subrouter()
|
2019-06-14 01:56:13 +00:00
|
|
|
if apper.App().cfg.App.OpenRegistration {
|
2018-11-08 06:31:01 +00:00
|
|
|
auth.HandleFunc("/signup", handler.All(apiSignup)).Methods("POST")
|
|
|
|
}
|
|
|
|
auth.HandleFunc("/login", handler.All(login)).Methods("POST")
|
|
|
|
auth.HandleFunc("/read", handler.WebErrors(handleWebCollectionUnlock, UserLevelNone)).Methods("POST")
|
|
|
|
auth.HandleFunc("/me", handler.All(handleAPILogout)).Methods("DELETE")
|
|
|
|
|
2018-11-08 06:19:03 +00:00
|
|
|
// Handle logged in user sections
|
|
|
|
me := write.PathPrefix("/me").Subrouter()
|
|
|
|
me.HandleFunc("/", handler.Redirect("/me", UserLevelUser))
|
|
|
|
me.HandleFunc("/c", handler.Redirect("/me/c/", UserLevelUser)).Methods("GET")
|
|
|
|
me.HandleFunc("/c/", handler.User(viewCollections)).Methods("GET")
|
|
|
|
me.HandleFunc("/c/{collection}", handler.User(viewEditCollection)).Methods("GET")
|
|
|
|
me.HandleFunc("/c/{collection}/stats", handler.User(viewStats)).Methods("GET")
|
|
|
|
me.HandleFunc("/posts", handler.Redirect("/me/posts/", UserLevelUser)).Methods("GET")
|
|
|
|
me.HandleFunc("/posts/", handler.User(viewArticles)).Methods("GET")
|
|
|
|
me.HandleFunc("/posts/export.csv", handler.Download(viewExportPosts, UserLevelUser)).Methods("GET")
|
|
|
|
me.HandleFunc("/posts/export.zip", handler.Download(viewExportPosts, UserLevelUser)).Methods("GET")
|
|
|
|
me.HandleFunc("/posts/export.json", handler.Download(viewExportPosts, UserLevelUser)).Methods("GET")
|
|
|
|
me.HandleFunc("/export", handler.User(viewExportOptions)).Methods("GET")
|
|
|
|
me.HandleFunc("/export.json", handler.Download(viewExportFull, UserLevelUser)).Methods("GET")
|
2019-08-16 21:27:24 +00:00
|
|
|
me.HandleFunc("/import", handler.User(viewImport)).Methods("GET")
|
2018-11-08 06:19:03 +00:00
|
|
|
me.HandleFunc("/settings", handler.User(viewSettings)).Methods("GET")
|
2019-01-18 05:05:50 +00:00
|
|
|
me.HandleFunc("/invites", handler.User(handleViewUserInvites)).Methods("GET")
|
2018-11-08 06:19:03 +00:00
|
|
|
me.HandleFunc("/logout", handler.Web(viewLogout, UserLevelNone)).Methods("GET")
|
|
|
|
|
|
|
|
write.HandleFunc("/api/me", handler.All(viewMeAPI)).Methods("GET")
|
|
|
|
apiMe := write.PathPrefix("/api/me/").Subrouter()
|
|
|
|
apiMe.HandleFunc("/", handler.All(viewMeAPI)).Methods("GET")
|
|
|
|
apiMe.HandleFunc("/posts", handler.UserAPI(viewMyPostsAPI)).Methods("GET")
|
|
|
|
apiMe.HandleFunc("/collections", handler.UserAPI(viewMyCollectionsAPI)).Methods("GET")
|
|
|
|
apiMe.HandleFunc("/password", handler.All(updatePassphrase)).Methods("POST")
|
|
|
|
apiMe.HandleFunc("/self", handler.All(updateSettings)).Methods("POST")
|
2019-01-18 05:05:50 +00:00
|
|
|
apiMe.HandleFunc("/invites", handler.User(handleCreateUserInvite)).Methods("POST")
|
2019-08-19 16:05:52 +00:00
|
|
|
apiMe.HandleFunc("/import", handler.User(handleImport)).Methods("POST")
|
2018-11-08 06:19:03 +00:00
|
|
|
|
|
|
|
// Sign up validation
|
|
|
|
write.HandleFunc("/api/alias", handler.All(handleUsernameCheck)).Methods("POST")
|
|
|
|
|
2019-12-17 20:27:34 +00:00
|
|
|
write.HandleFunc("/api/markdown", handler.All(handleRenderMarkdown)).Methods("POST")
|
2019-10-15 22:03:45 +00:00
|
|
|
|
2018-11-08 06:19:03 +00:00
|
|
|
// Handle collections
|
|
|
|
write.HandleFunc("/api/collections", handler.All(newCollection)).Methods("POST")
|
|
|
|
apiColls := write.PathPrefix("/api/collections/").Subrouter()
|
2019-06-17 00:24:47 +00:00
|
|
|
apiColls.HandleFunc("/{alias:[0-9a-zA-Z\\-]+}", handler.AllReader(fetchCollection)).Methods("GET")
|
2018-11-08 06:19:03 +00:00
|
|
|
apiColls.HandleFunc("/{alias:[0-9a-zA-Z\\-]+}", handler.All(existingCollection)).Methods("POST", "DELETE")
|
2019-06-17 00:24:47 +00:00
|
|
|
apiColls.HandleFunc("/{alias}/posts", handler.AllReader(fetchCollectionPosts)).Methods("GET")
|
2018-11-08 06:19:03 +00:00
|
|
|
apiColls.HandleFunc("/{alias}/posts", handler.All(newPost)).Methods("POST")
|
2019-06-17 00:24:47 +00:00
|
|
|
apiColls.HandleFunc("/{alias}/posts/{post}", handler.AllReader(fetchPost)).Methods("GET")
|
2018-11-08 06:19:03 +00:00
|
|
|
apiColls.HandleFunc("/{alias}/posts/{post:[a-zA-Z0-9]{10}}", handler.All(existingPost)).Methods("POST")
|
2019-06-17 00:24:47 +00:00
|
|
|
apiColls.HandleFunc("/{alias}/posts/{post}/{property}", handler.AllReader(fetchPostProperty)).Methods("GET")
|
2018-11-08 06:19:03 +00:00
|
|
|
apiColls.HandleFunc("/{alias}/collect", handler.All(addPost)).Methods("POST")
|
|
|
|
apiColls.HandleFunc("/{alias}/pin", handler.All(pinPost)).Methods("POST")
|
|
|
|
apiColls.HandleFunc("/{alias}/unpin", handler.All(pinPost)).Methods("POST")
|
2018-11-08 06:28:08 +00:00
|
|
|
apiColls.HandleFunc("/{alias}/inbox", handler.All(handleFetchCollectionInbox)).Methods("POST")
|
2019-06-17 00:24:47 +00:00
|
|
|
apiColls.HandleFunc("/{alias}/outbox", handler.AllReader(handleFetchCollectionOutbox)).Methods("GET")
|
|
|
|
apiColls.HandleFunc("/{alias}/following", handler.AllReader(handleFetchCollectionFollowing)).Methods("GET")
|
|
|
|
apiColls.HandleFunc("/{alias}/followers", handler.AllReader(handleFetchCollectionFollowers)).Methods("GET")
|
2018-11-08 06:19:03 +00:00
|
|
|
|
2018-11-08 04:43:11 +00:00
|
|
|
// Handle posts
|
|
|
|
write.HandleFunc("/api/posts", handler.All(newPost)).Methods("POST")
|
|
|
|
posts := write.PathPrefix("/api/posts/").Subrouter()
|
2019-06-17 00:24:47 +00:00
|
|
|
posts.HandleFunc("/{post:[a-zA-Z0-9]{10}}", handler.AllReader(fetchPost)).Methods("GET")
|
2018-11-08 04:43:11 +00:00
|
|
|
posts.HandleFunc("/{post:[a-zA-Z0-9]{10}}", handler.All(existingPost)).Methods("POST", "PUT")
|
|
|
|
posts.HandleFunc("/{post:[a-zA-Z0-9]{10}}", handler.All(deletePost)).Methods("DELETE")
|
2019-06-17 00:24:47 +00:00
|
|
|
posts.HandleFunc("/{post:[a-zA-Z0-9]{10}}/{property}", handler.AllReader(fetchPostProperty)).Methods("GET")
|
2018-11-08 04:43:11 +00:00
|
|
|
posts.HandleFunc("/claim", handler.All(addPost)).Methods("POST")
|
|
|
|
posts.HandleFunc("/disperse", handler.All(dispersePost)).Methods("POST")
|
|
|
|
|
2019-01-18 05:05:50 +00:00
|
|
|
write.HandleFunc("/auth/signup", handler.Web(handleWebSignup, UserLevelNoneRequired)).Methods("POST")
|
2018-11-08 06:31:01 +00:00
|
|
|
write.HandleFunc("/auth/login", handler.Web(webLogin, UserLevelNoneRequired)).Methods("POST")
|
|
|
|
|
2018-11-19 01:18:22 +00:00
|
|
|
write.HandleFunc("/admin", handler.Admin(handleViewAdminDash)).Methods("GET")
|
2020-02-14 18:55:24 +00:00
|
|
|
write.HandleFunc("/admin/monitor", handler.Admin(handleViewAdminMonitor)).Methods("GET")
|
|
|
|
write.HandleFunc("/admin/settings", handler.Admin(handleViewAdminSettings)).Methods("GET")
|
2019-01-05 03:28:29 +00:00
|
|
|
write.HandleFunc("/admin/users", handler.Admin(handleViewAdminUsers)).Methods("GET")
|
|
|
|
write.HandleFunc("/admin/user/{username}", handler.Admin(handleViewAdminUser)).Methods("GET")
|
2019-10-25 19:04:24 +00:00
|
|
|
write.HandleFunc("/admin/user/{username}/status", handler.Admin(handleAdminToggleUserStatus)).Methods("POST")
|
2019-11-11 07:02:22 +00:00
|
|
|
write.HandleFunc("/admin/user/{username}/passphrase", handler.Admin(handleAdminResetUserPass)).Methods("POST")
|
2019-04-06 17:23:22 +00:00
|
|
|
write.HandleFunc("/admin/pages", handler.Admin(handleViewAdminPages)).Methods("GET")
|
|
|
|
write.HandleFunc("/admin/page/{slug}", handler.Admin(handleViewAdminPage)).Methods("GET")
|
2019-06-14 01:56:13 +00:00
|
|
|
write.HandleFunc("/admin/update/config", handler.AdminApper(handleAdminUpdateConfig)).Methods("POST")
|
2018-11-19 02:58:50 +00:00
|
|
|
write.HandleFunc("/admin/update/{page}", handler.Admin(handleAdminUpdateSite)).Methods("POST")
|
2019-08-29 22:05:59 +00:00
|
|
|
write.HandleFunc("/admin/updates", handler.Admin(handleViewAdminUpdates)).Methods("GET")
|
2018-11-19 01:18:22 +00:00
|
|
|
|
2018-11-08 06:31:01 +00:00
|
|
|
// Handle special pages first
|
|
|
|
write.HandleFunc("/login", handler.Web(viewLogin, UserLevelNoneRequired))
|
2019-08-09 16:00:46 +00:00
|
|
|
write.HandleFunc("/signup", handler.Web(handleViewLanding, UserLevelNoneRequired))
|
2020-01-29 14:11:02 +00:00
|
|
|
write.HandleFunc("/invite/{code:[a-zA-Z0-9]+}", handler.Web(handleViewInvite, UserLevelOptional)).Methods("GET")
|
2018-12-10 21:02:42 +00:00
|
|
|
// TODO: show a reader-specific 404 page if the function is disabled
|
2019-06-19 23:26:10 +00:00
|
|
|
write.HandleFunc("/read", handler.Web(viewLocalTimeline, UserLevelReader))
|
|
|
|
RouteRead(handler, UserLevelReader, write.PathPrefix("/read").Subrouter())
|
2018-11-08 06:31:01 +00:00
|
|
|
|
2018-11-10 03:10:46 +00:00
|
|
|
draftEditPrefix := ""
|
2019-06-14 01:56:13 +00:00
|
|
|
if apper.App().cfg.App.SingleUser {
|
2018-11-10 03:10:46 +00:00
|
|
|
draftEditPrefix = "/d"
|
2020-01-16 19:47:23 +00:00
|
|
|
write.HandleFunc("/me/new", handler.Web(handleViewPad, UserLevelUser)).Methods("GET")
|
2018-11-08 05:11:42 +00:00
|
|
|
} else {
|
2020-01-16 19:47:23 +00:00
|
|
|
write.HandleFunc("/new", handler.Web(handleViewPad, UserLevelUser)).Methods("GET")
|
2018-11-08 05:11:42 +00:00
|
|
|
}
|
|
|
|
|
2018-11-08 04:43:11 +00:00
|
|
|
// All the existing stuff
|
2020-02-06 22:44:02 +00:00
|
|
|
write.HandleFunc(draftEditPrefix+"/{action}/edit", handler.Web(handleViewPad, UserLevelUser)).Methods("GET")
|
|
|
|
write.HandleFunc(draftEditPrefix+"/{action}/meta", handler.Web(handleViewMeta, UserLevelUser)).Methods("GET")
|
2018-11-08 04:43:11 +00:00
|
|
|
// Collections
|
2019-06-14 01:56:13 +00:00
|
|
|
if apper.App().cfg.App.SingleUser {
|
2018-11-08 06:19:03 +00:00
|
|
|
RouteCollections(handler, write.PathPrefix("/").Subrouter())
|
2018-11-08 04:43:11 +00:00
|
|
|
} else {
|
2019-06-16 22:55:50 +00:00
|
|
|
write.HandleFunc("/{prefix:[@~$!\\-+]}{collection}", handler.Web(handleViewCollection, UserLevelReader))
|
|
|
|
write.HandleFunc("/{collection}/", handler.Web(handleViewCollection, UserLevelReader))
|
2018-11-08 06:19:03 +00:00
|
|
|
RouteCollections(handler, write.PathPrefix("/{prefix:[@~$!\\-+]?}{collection}").Subrouter())
|
2018-11-08 04:43:11 +00:00
|
|
|
// Posts
|
|
|
|
}
|
2018-11-10 03:10:46 +00:00
|
|
|
write.HandleFunc(draftEditPrefix+"/{post}", handler.Web(handleViewPost, UserLevelOptional))
|
2018-11-08 05:11:42 +00:00
|
|
|
write.HandleFunc("/", handler.Web(handleViewHome, UserLevelOptional))
|
2019-12-28 20:15:47 +00:00
|
|
|
|
2019-06-13 22:50:23 +00:00
|
|
|
return r
|
2018-10-15 18:44:15 +00:00
|
|
|
}
|
2018-11-08 06:19:03 +00:00
|
|
|
|
|
|
|
func RouteCollections(handler *Handler, r *mux.Router) {
|
2019-06-16 22:55:50 +00:00
|
|
|
r.HandleFunc("/page/{page:[0-9]+}", handler.Web(handleViewCollection, UserLevelReader))
|
|
|
|
r.HandleFunc("/tag:{tag}", handler.Web(handleViewCollectionTag, UserLevelReader))
|
|
|
|
r.HandleFunc("/tag:{tag}/feed/", handler.Web(ViewFeed, UserLevelReader))
|
|
|
|
r.HandleFunc("/tags/{tag}", handler.Web(handleViewCollectionTag, UserLevelReader))
|
2019-06-17 01:16:23 +00:00
|
|
|
r.HandleFunc("/sitemap.xml", handler.AllReader(handleViewSitemap))
|
|
|
|
r.HandleFunc("/feed/", handler.AllReader(ViewFeed))
|
2019-06-16 22:55:50 +00:00
|
|
|
r.HandleFunc("/{slug}", handler.CollectionPostOrStatic)
|
2018-11-08 06:19:03 +00:00
|
|
|
r.HandleFunc("/{slug}/edit", handler.Web(handleViewPad, UserLevelUser))
|
|
|
|
r.HandleFunc("/{slug}/edit/meta", handler.Web(handleViewMeta, UserLevelUser))
|
2019-06-16 22:55:50 +00:00
|
|
|
r.HandleFunc("/{slug}/", handler.Web(handleCollectionPostRedirect, UserLevelReader)).Methods("GET")
|
2018-11-08 06:19:03 +00:00
|
|
|
}
|
2018-12-10 21:02:42 +00:00
|
|
|
|
2019-06-16 22:55:50 +00:00
|
|
|
func RouteRead(handler *Handler, readPerm UserLevelFunc, r *mux.Router) {
|
2018-12-10 21:02:42 +00:00
|
|
|
r.HandleFunc("/api/posts", handler.Web(viewLocalTimelineAPI, readPerm))
|
|
|
|
r.HandleFunc("/p/{page}", handler.Web(viewLocalTimeline, readPerm))
|
|
|
|
r.HandleFunc("/feed/", handler.Web(viewLocalTimelineFeed, readPerm))
|
|
|
|
r.HandleFunc("/t/{tag}", handler.Web(viewLocalTimeline, readPerm))
|
|
|
|
r.HandleFunc("/a/{post}", handler.Web(handlePostIDRedirect, readPerm))
|
|
|
|
r.HandleFunc("/{author}", handler.Web(viewLocalTimeline, readPerm))
|
|
|
|
r.HandleFunc("/", handler.Web(viewLocalTimeline, readPerm))
|
|
|
|
}
|