github.com/elliott5/community@v0.14.1-0.20160709191136-823126fb026a/documize/api/endpoint/router.go (about)

     1  // Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
     2  //
     3  // This software (Documize Community Edition) is licensed under
     4  // GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
     5  //
     6  // You can operate outside the AGPL restrictions by purchasing
     7  // Documize Enterprise Edition and obtaining a commercial license
     8  // by contacting <sales@documize.com>.
     9  //
    10  // https://documize.com
    11  
    12  package endpoint
    13  
    14  import (
    15  	"fmt"
    16  	"net/http"
    17  	"os"
    18  	"strings"
    19  
    20  	"github.com/codegangsta/negroni"
    21  	"github.com/documize/community/documize/api/plugins"
    22  	"github.com/documize/community/documize/database"
    23  	"github.com/documize/community/documize/web"
    24  	"github.com/documize/community/wordsmith/environment"
    25  	"github.com/documize/community/wordsmith/log"
    26  	"github.com/gorilla/mux"
    27  )
    28  
    29  const (
    30  	// AppVersion does what it says
    31  	// Note: versioning scheme is not http://semver.org
    32  	AppVersion = "0.14.0"
    33  )
    34  
    35  var port, certFile, keyFile, forcePort2SSL string
    36  
    37  func init() {
    38  	environment.GetString(&certFile, "cert", false, "the cert.pem file used for https", nil)
    39  	environment.GetString(&keyFile, "key", false, "the key.pem file used for https", nil)
    40  	environment.GetString(&port, "port", false, "http/https port number", nil)
    41  	environment.GetString(&forcePort2SSL, "forcesslport", false, "redirect given http port number to TLS", nil)
    42  }
    43  
    44  var testHost string // used during automated testing
    45  
    46  // Serve the Documize endpoint.
    47  func Serve(ready chan struct{}) {
    48  	err := plugins.LibSetup()
    49  
    50  	if err != nil {
    51  		log.Error("Terminating before running - invalid plugin.json", err)
    52  		os.Exit(1)
    53  	}
    54  
    55  	log.Info(fmt.Sprintf("Documize version %s", AppVersion))
    56  
    57  	router := mux.NewRouter()
    58  
    59  	router.PathPrefix("/api/public/").Handler(negroni.New(
    60  		negroni.HandlerFunc(cors),
    61  		negroni.Wrap(buildUnsecureRoutes()),
    62  	))
    63  
    64  	router.PathPrefix("/api").Handler(negroni.New(
    65  		negroni.HandlerFunc(Authorize),
    66  		negroni.Wrap(buildSecureRoutes()),
    67  	))
    68  
    69  	router.PathPrefix("/").Handler(negroni.New(
    70  		negroni.HandlerFunc(cors),
    71  		negroni.Wrap(AppRouter()),
    72  	))
    73  
    74  	n := negroni.New()
    75  	n.Use(negroni.NewStatic(web.StaticAssetsFileSystem()))
    76  	n.Use(negroni.HandlerFunc(cors))
    77  	n.Use(negroni.HandlerFunc(metrics))
    78  	n.UseHandler(router)
    79  	ready <- struct{}{}
    80  
    81  	if certFile == "" && keyFile == "" {
    82  		if port == "" {
    83  			port = "80"
    84  		}
    85  
    86  		log.Info("Starting non-SSL server on " + port)
    87  
    88  		n.Run(testHost + ":" + port)
    89  	} else {
    90  		if port == "" {
    91  			port = "443"
    92  		}
    93  
    94  		if forcePort2SSL != "" {
    95  			log.Info("Starting non-SSL server on " + forcePort2SSL + " and redirecting to SSL server on  " + port)
    96  
    97  			go func() {
    98  				err := http.ListenAndServe(":"+forcePort2SSL, http.HandlerFunc(
    99  					func(w http.ResponseWriter, req *http.Request) {
   100  						var host = strings.Replace(req.Host, forcePort2SSL, port, 1) + req.RequestURI
   101  						http.Redirect(w, req, "https://"+host, http.StatusMovedPermanently)
   102  					}))
   103  				if err != nil {
   104  					log.Error("ListenAndServe on "+forcePort2SSL, err)
   105  				}
   106  			}()
   107  		}
   108  
   109  		log.Info("Starting SSL server on " + port + " with " + certFile + " " + keyFile)
   110  
   111  		server := &http.Server{Addr: ":" + port, Handler: n}
   112  		server.SetKeepAlivesEnabled(true)
   113  		if err := server.ListenAndServeTLS(certFile, keyFile); err != nil {
   114  			log.Error("ListenAndServeTLS on "+port, err)
   115  		}
   116  	}
   117  }
   118  
   119  func buildUnsecureRoutes() *mux.Router {
   120  	router := mux.NewRouter()
   121  
   122  	router.HandleFunc("/api/public/meta", GetMeta).Methods("GET", "OPTIONS")
   123  	router.HandleFunc("/api/public/authenticate", Authenticate).Methods("POST", "OPTIONS")
   124  	router.HandleFunc("/api/public/validate", ValidateAuthToken).Methods("GET", "OPTIONS")
   125  	router.HandleFunc("/api/public/forgot", ForgotUserPassword).Methods("POST", "OPTIONS")
   126  	router.HandleFunc("/api/public/reset/{token}", ResetUserPassword).Methods("POST", "OPTIONS")
   127  	router.HandleFunc("/api/public/share/{folderID}", AcceptSharedFolder).Methods("POST", "OPTIONS")
   128  	router.HandleFunc("/api/public/attachments/{orgID}/{job}/{fileID}", AttachmentDownload).Methods("GET", "OPTIONS")
   129  	router.HandleFunc("/api/public/version", version).Methods("GET", "OPTIONS")
   130  
   131  	return router
   132  }
   133  
   134  func buildSecureRoutes() *mux.Router {
   135  	router := mux.NewRouter()
   136  
   137  	// Import & Convert Document
   138  	router.HandleFunc("/api/import/folder/{folderID}", UploadConvertDocument).Methods("POST", "OPTIONS")
   139  
   140  	// Document
   141  	router.HandleFunc("/api/documents/{documentID}/export", GetDocumentAsDocx).Methods("GET", "OPTIONS")
   142  	router.HandleFunc("/api/documents", GetDocumentsByTag).Methods("GET", "OPTIONS").Queries("filter", "tag")
   143  	router.HandleFunc("/api/documents", GetDocumentsByFolder).Methods("GET", "OPTIONS")
   144  	router.HandleFunc("/api/documents/{documentID}", GetDocument).Methods("GET", "OPTIONS")
   145  	router.HandleFunc("/api/documents/{documentID}", UpdateDocument).Methods("PUT", "OPTIONS")
   146  	router.HandleFunc("/api/documents/{documentID}", DeleteDocument).Methods("DELETE", "OPTIONS")
   147  
   148  	// Document Meta
   149  	router.HandleFunc("/api/documents/{documentID}/meta", GetDocumentMeta).Methods("GET", "OPTIONS")
   150  
   151  	// Document Page
   152  	router.HandleFunc("/api/documents/{documentID}/pages/level", ChangeDocumentPageLevel).Methods("POST", "OPTIONS")
   153  	router.HandleFunc("/api/documents/{documentID}/pages/sequence", ChangeDocumentPageSequence).Methods("POST", "OPTIONS")
   154  	router.HandleFunc("/api/documents/{documentID}/pages/batch", GetDocumentPagesBatch).Methods("POST", "OPTIONS")
   155  	// router.HandleFunc("/api/documents/{documentID}/pages/{pageID}/revisions", GetDocumentPageRevisions).Methods("GET", "OPTIONS")
   156  	// router.HandleFunc("/api/documents/{documentID}/pages/{pageID}/revisions/{revisionID}", GetDocumentPageDiff).Methods("GET", "OPTIONS")
   157  	// router.HandleFunc("/api/documents/{documentID}/pages/{pageID}/revisions/{revisionID}", RollbackDocumentPage).Methods("POST", "OPTIONS")
   158  	router.HandleFunc("/api/documents/{documentID}/pages", GetDocumentPages).Methods("GET", "OPTIONS")
   159  	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}", UpdateDocumentPage).Methods("PUT", "OPTIONS")
   160  	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}", DeleteDocumentPage).Methods("DELETE", "OPTIONS")
   161  	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}", DeleteDocumentPages).Methods("POST", "OPTIONS")
   162  	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}", GetDocumentPage).Methods("GET", "OPTIONS")
   163  	router.HandleFunc("/api/documents/{documentID}/pages", AddDocumentPage).Methods("POST", "OPTIONS")
   164  	router.HandleFunc("/api/documents/{documentID}/attachments", GetAttachments).Methods("GET", "OPTIONS")
   165  	router.HandleFunc("/api/documents/{documentID}/attachments/{attachmentID}", DeleteAttachment).Methods("DELETE", "OPTIONS")
   166  	router.HandleFunc("/api/documents/{documentID}/attachments", AddAttachments).Methods("POST", "OPTIONS")
   167  
   168  	// Document Meta
   169  	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}/meta", GetDocumentPageMeta).Methods("GET", "OPTIONS")
   170  
   171  	// Organization
   172  	router.HandleFunc("/api/organizations/{orgID}", GetOrganization).Methods("GET", "OPTIONS")
   173  	router.HandleFunc("/api/organizations/{orgID}", UpdateOrganization).Methods("PUT", "OPTIONS")
   174  
   175  	// Folder
   176  	router.HandleFunc("/api/folders/{folderID}/move/{moveToId}", RemoveFolder).Methods("DELETE", "OPTIONS")
   177  	router.HandleFunc("/api/folders/{folderID}/permissions", SetFolderPermissions).Methods("PUT", "OPTIONS")
   178  	router.HandleFunc("/api/folders/{folderID}/permissions", GetFolderPermissions).Methods("GET", "OPTIONS")
   179  	router.HandleFunc("/api/folders/{folderID}/invitation", InviteToFolder).Methods("POST", "OPTIONS")
   180  	router.HandleFunc("/api/folders", GetFolderVisibility).Methods("GET", "OPTIONS").Queries("filter", "viewers")
   181  	router.HandleFunc("/api/folders", AddFolder).Methods("POST", "OPTIONS")
   182  	router.HandleFunc("/api/folders", GetFolders).Methods("GET", "OPTIONS")
   183  	router.HandleFunc("/api/folders/{folderID}", GetFolder).Methods("GET", "OPTIONS")
   184  	router.HandleFunc("/api/folders/{folderID}", UpdateFolder).Methods("PUT", "OPTIONS")
   185  
   186  	// Users
   187  	router.HandleFunc("/api/users/{userID}/password", ChangeUserPassword).Methods("POST", "OPTIONS")
   188  	router.HandleFunc("/api/users/{userID}/permissions", GetUserFolderPermissions).Methods("GET", "OPTIONS")
   189  	router.HandleFunc("/api/users", AddUser).Methods("POST", "OPTIONS")
   190  	router.HandleFunc("/api/users/folder/{folderID}", GetFolderUsers).Methods("GET", "OPTIONS")
   191  	router.HandleFunc("/api/users", GetOrganizationUsers).Methods("GET", "OPTIONS")
   192  	router.HandleFunc("/api/users/{userID}", GetUser).Methods("GET", "OPTIONS")
   193  	router.HandleFunc("/api/users/{userID}", UpdateUser).Methods("PUT", "OPTIONS")
   194  	router.HandleFunc("/api/users/{userID}", DeleteUser).Methods("DELETE", "OPTIONS")
   195  
   196  	// Search
   197  	router.HandleFunc("/api/search", SearchDocuments).Methods("GET", "OPTIONS")
   198  
   199  	// Templates
   200  	router.HandleFunc("/api/templates", SaveAsTemplate).Methods("POST", "OPTIONS")
   201  	router.HandleFunc("/api/templates", GetSavedTemplates).Methods("GET", "OPTIONS")
   202  	router.HandleFunc("/api/templates/stock", GetStockTemplates).Methods("GET", "OPTIONS")
   203  	router.HandleFunc("/api/templates/{templateID}/folder/{folderID}", StartDocumentFromStockTemplate).Methods("POST", "OPTIONS").Queries("type", "stock")
   204  	router.HandleFunc("/api/templates/{templateID}/folder/{folderID}", StartDocumentFromSavedTemplate).Methods("POST", "OPTIONS").Queries("type", "saved")
   205  
   206  	// Sections
   207  	router.HandleFunc("/api/sections", GetSections).Methods("GET", "OPTIONS")
   208  	router.HandleFunc("/api/sections", RunSectionCommand).Methods("POST", "OPTIONS")
   209  	router.HandleFunc("/api/sections/refresh", RefreshSections).Methods("GET", "OPTIONS")
   210  
   211  	return router
   212  }
   213  
   214  func cors(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
   215  	w.Header().Set("Access-Control-Allow-Origin", "*")
   216  	w.Header().Set("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS, PATCH")
   217  	w.Header().Set("Access-Control-Allow-Headers", "host, content-type, accept, authorization, origin, referer, user-agent, cache-control, x-requested-with")
   218  	w.Header().Set("Access-Control-Expose-Headers", "x-documize-version")
   219  
   220  	if r.Method == "OPTIONS" {
   221  		if _, err := w.Write([]byte("")); err != nil {
   222  			log.Error("cors", err)
   223  		}
   224  		return
   225  	}
   226  
   227  	next(w, r)
   228  }
   229  
   230  func metrics(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
   231  	w.Header().Add("X-Documize-Version", AppVersion)
   232  	w.Header().Add("Cache-Control", "no-cache")
   233  
   234  	// if certFile != "" && keyFile != "" {
   235  	// 	w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
   236  	// }
   237  
   238  	next(w, r)
   239  }
   240  
   241  func version(w http.ResponseWriter, r *http.Request) {
   242  	if _, err := w.Write([]byte(AppVersion)); err != nil {
   243  		log.Error("versionHandler", err)
   244  	}
   245  }
   246  
   247  // AppRouter configures single page app handler.
   248  func AppRouter() *mux.Router {
   249  
   250  	router := mux.NewRouter()
   251  
   252  	switch web.SiteMode {
   253  	case web.SiteModeOffline:
   254  		log.Info("Serving OFFLINE web app")
   255  	case web.SiteModeSetup:
   256  		log.Info("Serving SETUP web app")
   257  		router.HandleFunc("/setup", database.Create).Methods("POST", "OPTIONS")
   258  	case web.SiteModeBadDB:
   259  		log.Info("Serving BAD DATABASE web app")
   260  	default:
   261  		log.Info("Starting web app")
   262  	}
   263  
   264  	router.HandleFunc("/robots.txt", GetRobots).Methods("GET", "OPTIONS")
   265  	router.HandleFunc("/sitemap.xml", GetSitemap).Methods("GET", "OPTIONS")
   266  	router.HandleFunc("/{rest:.*}", web.EmberHandler)
   267  
   268  	return router
   269  }