/* Copyright (c) 2019 Ben Morrison (gbmor) This file is part of Getwtxt. Getwtxt is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Getwtxt is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Getwtxt. If not, see . */ package svc // import "git.sr.ht/~gbmor/getwtxt/svc" import ( "fmt" "log" "net/http" "time" "github.com/gorilla/handlers" "github.com/gorilla/mux" ) // Start is the initialization function for getwtxt func Start() { before := time.Now() initSvc() // StrictSlash(true) allows /api and /api/ // to serve the same content without duplicating // handlers/paths index := mux.NewRouter().StrictSlash(true) setIndexRouting(index) // Serve the raw contents of this directory so users can // place miscellaneous files in it to use with the template. index.PathPrefix("/static"). Methods("GET", "HEAD"). Handler(http.StripPrefix("/static", http.FileServer(http.Dir(confObj.StaticDir)))) api := index.PathPrefix("/api").Subrouter() setEndpointRouting(api) confObj.Mu.RLock() portnum := fmt.Sprintf(":%v", confObj.Port) confObj.Mu.RUnlock() server := newServer(portnum, index) log.Printf("*** Listening on %v\n", portnum) log.Printf("*** getwtxt %v Startup finished at %v, took %v\n\n", Vers, time.Now().Format(time.RFC3339), time.Since(before)) errLog("", server.ListenAndServe()) closeLog <- struct{}{} killTickers() killDB() close(dbChan) close(closeLog) } func newServer(port string, index *mux.Router) *http.Server { // handlers.CompressHandler gzips all responses. // ipMiddleware passes the request IP along. // Write/Read timeouts are self explanatory. return &http.Server{ Handler: handlers.CompressHandler(ipMiddleware(index)), Addr: port, WriteTimeout: 15 * time.Second, ReadTimeout: 15 * time.Second, } } func setIndexRouting(index *mux.Router) { index.Path("/"). Methods("GET", "HEAD"). HandlerFunc(staticHandler) index.Path("/css"). Methods("GET", "HEAD"). HandlerFunc(staticHandler) index.Path("/api"). Methods("GET", "HEAD"). HandlerFunc(apiBaseHandler) } func setEndpointRouting(api *mux.Router) { api.Path("/admin/users"). Methods("DELETE"). HandlerFunc(handleUserDelete) // May add support for other formats later. // Making this future-proof. api.Path("/{format:(?:plain)}"). Methods("GET", "HEAD"). HandlerFunc(apiFormatHandler) // Non-standard API call to list *all* tweets // in a single request. api.Path("/{format:(?:plain)}/tweets/all"). Methods("GET", "HEAD"). HandlerFunc(apiAllTweetsHandler) // Specifying the endpoint with and without query information. // Will return 404 on empty queries otherwise. api.Path("/{format:(?:plain)}/{endpoint:(?:mentions|users|tweets|version)}"). Methods("GET", "HEAD"). HandlerFunc(apiEndpointHandler) api.Path("/{format:(?:plain)}/{endpoint:(?:mentions|users|tweets)}"). Queries("url", "{url}", "q", "{query}", "page", "{[0-9]+}"). Methods("GET", "HEAD"). HandlerFunc(apiEndpointHandler) // This is for submitting new users. Both query variables must exist // in the request for this to match. api.Path("/{format:(?:plain)}/{endpoint:users}"). Queries("url", "{url}", "nickname", "{nickname:[a-zA-Z0-9_-]+}"). Methods("POST"). HandlerFunc(apiEndpointPOSTHandler) // This is for submitting new users incorrectly // and letting the requester know about their error. api.Path("/{format:(?:plain)}/{endpoint:users}"). Queries("url", "{url}"). Methods("POST"). HandlerFunc(apiEndpointPOSTHandler) // This is also for submitting new users incorrectly // and letting the requester know about their error. api.Path("/{format:(?:plain)}/{endpoint:users}"). Queries("nickname", "{nickname:[a-zA-Z0-9_-]+}"). Methods("POST"). HandlerFunc(apiEndpointPOSTHandler) // Show all observed tags api.Path("/{format:(?:plain)}/tags"). Methods("GET", "HEAD"). HandlerFunc(apiTagsBaseHandler) // Show Nth page of all observed tags api.Path("/{format:(?:plain)}/tags"). Queries("page", "{[0-9]+}"). Methods("GET", "HEAD"). HandlerFunc(apiTagsBaseHandler) // Requests statuses with a specific tag api.Path("/{format:(?:plain)}/tags/{tags:[a-zA-Z0-9_-]+}"). Methods("GET", "HEAD"). HandlerFunc(apiTagsHandler) // Requests Nth page of statuses with a specific tag api.Path("/{format:(?:plain)}/tags/{tags:[a-zA-Z0-9_-]+}"). Queries("page", "{[0-9]+}"). Methods("GET", "HEAD"). HandlerFunc(apiTagsHandler) }