summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--go.mod2
-rw-r--r--go.sum8
-rw-r--r--http.go11
-rw-r--r--init.go8
-rw-r--r--main.go16
-rw-r--r--query.go83
6 files changed, 115 insertions, 13 deletions
diff --git a/go.mod b/go.mod
index a61e192..204565f 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,9 @@ module github.com/gbmor/getwtxt
 go 1.11
 
 require (
+	github.com/BurntSushi/toml v0.3.1 // indirect
 	github.com/fsnotify/fsnotify v1.4.7
+	github.com/getwtxt/registry v0.0.0-20190520044831-747da48b0a7c
 	github.com/gorilla/handlers v1.4.0
 	github.com/gorilla/mux v1.7.1
 	github.com/spf13/pflag v1.0.3
diff --git a/go.sum b/go.sum
index 35b6f2b..f6c24d2 100644
--- a/go.sum
+++ b/go.sum
@@ -1,10 +1,15 @@
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/getwtxt/registry v0.0.0-20190520044831-747da48b0a7c h1:/ebTy3JpIAlaiD6jH5xGK2AL4/EQ74gBGW4ncPnD5mE=
+github.com/getwtxt/registry v0.0.0-20190520044831-747da48b0a7c/go.mod h1:BGSIALOFqIRj+ACLB8etWGUOgFAKN8oFDpCsw6YOdYQ=
 github.com/gorilla/handlers v1.4.0 h1:XulKRWSQK5uChr4pEgSE4Tc/OcmnU9GJuSwdog/tZsA=
 github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
 github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU=
@@ -17,6 +22,7 @@ github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQz
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
@@ -28,6 +34,7 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
@@ -36,6 +43,7 @@ golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a h1:1n5lsVfiQW3yfsRGu98756EH1
 golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/http.go b/http.go
index 52f9923..b338796 100644
--- a/http.go
+++ b/http.go
@@ -108,15 +108,16 @@ func apiFormatHandler(w http.ResponseWriter, r *http.Request) {
 
 // handles "/api/plain/(users|mentions|tweets)"
 func apiEndpointHandler(w http.ResponseWriter, r *http.Request) {
-	vars := mux.Vars(r)
-	format := vars["format"]
-	endpoint := vars["endpoint"]
-
 	uip := getIPFromCtx(r.Context())
 	log.Printf("Request from %v :: %v %v\n", uip, r.Method, r.URL)
 
+	if r.FormValue("q") != "" || r.FormValue("url") != "" {
+		apiEndpointQuery(w, r)
+		return
+	}
+
 	w.Header().Set("Content-Type", htmlutf8)
-	n, err := w.Write([]byte(format + "/" + endpoint))
+	n, err := w.Write([]byte(r.URL.String()))
 	if err != nil || n == 0 {
 		log.Printf("500: Error writing to HTTP stream: %v, %v %v via %v\n", err, r.Method, r.URL, uip)
 		http.Error(w, err.Error(), http.StatusInternalServerError)
diff --git a/init.go b/init.go
index ee76f01..60530ea 100644
--- a/init.go
+++ b/init.go
@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"github.com/fsnotify/fsnotify"
+	"github.com/getwtxt/registry"
 	"github.com/spf13/pflag"
 	"github.com/spf13/viper"
 )
@@ -28,6 +29,9 @@ var closelog = make(chan bool, 1)
 // templates
 var tmpls *template.Template
 
+// registry index
+var twtxtCache = registry.NewIndex()
+
 func init() {
 	checkFlags()
 	titleScreen()
@@ -156,10 +160,12 @@ func watchForInterrupt() {
 			if !confObj.stdoutLogging {
 				// signal to close the log file
 				closelog <- true
-				time.Sleep(20 * time.Millisecond)
 			}
 
 			close(closelog)
+
+			// Let everything catch up
+			time.Sleep(30 * time.Millisecond)
 			os.Exit(0)
 		}
 	}()
diff --git a/main.go b/main.go
index 4508edf..448ff1a 100644
--- a/main.go
+++ b/main.go
@@ -20,41 +20,49 @@ func main() {
 	index := mux.NewRouter().StrictSlash(true)
 	api := index.PathPrefix("/api").Subrouter()
 
-	// <3 gorilla/mux
+	// Begin the path -> handler mapping
 	index.Path("/").
 		Methods("GET").
 		HandlerFunc(indexHandler)
+
 	index.Path("/css").
 		Methods("GET").
 		HandlerFunc(cssHandler)
+
 	index.Path("/api").
 		Methods("GET").
 		HandlerFunc(apiBaseHandler)
+
 	// twtxt will add support for other formats later.
 	// Maybe json? Making this future-proof.
 	api.Path("/{format:(?:plain)}").
 		Methods("GET").
 		HandlerFunc(apiFormatHandler)
+
 	// Specifying the endpoint with and without query information.
 	// Will return 404 on empty queries otherwise.
 	api.Path("/{format:(?:plain)}/{endpoint:(?:mentions|users|tweets)}").
 		Methods("GET").
 		HandlerFunc(apiEndpointHandler)
+
 	// Using stdlib net/url to validate the input URLs rather than regex.
 	// Validating a URL with regex is unwieldly
 	api.Path("/{format:(?:plain)}/{endpoint:(?:mentions|users|tweets)}").
 		Queries("url", "{url}", "q", "{query}").
 		Methods("GET").
 		HandlerFunc(apiEndpointHandler)
+
 	// This is for submitting new users
 	api.Path("/{format:(?:plain)}/{endpoint:users}").
 		Queries("url", "{url}", "nickname", "{nickname:[a-zA-Z0-9_-]+}").
 		Methods("POST").
 		HandlerFunc(apiEndpointPOSTHandler)
+
 	// Show all observed tags
 	api.Path("/{format:(?:plain)}/tags").
 		Methods("GET").
 		HandlerFunc(apiTagsBaseHandler)
+
 	// Requests tweets with a specific tag
 	api.Path("/{format:(?:plain)}/tags/{tags:[a-zA-Z0-9_-]+}").
 		Methods("GET").
@@ -77,12 +85,6 @@ func main() {
 	if err != nil {
 		log.Printf("%v\n", err)
 	}
-	defer func() {
-		err := server.Close()
-		if err != nil {
-			log.Printf("%v\n", err)
-		}
-	}()
 
 	closelog <- true
 }
diff --git a/query.go b/query.go
new file mode 100644
index 0000000..63deb55
--- /dev/null
+++ b/query.go
@@ -0,0 +1,83 @@
+package main
+
+import (
+	"log"
+	"net/http"
+	"strings"
+
+	"github.com/gorilla/mux"
+)
+
+func apiErrCheck(err error, r *http.Request) {
+	if err != nil {
+		uip := getIPFromCtx(r.Context())
+		log.Printf("%v :: %v %v :: %v\n", uip, r.Method, r.URL, err)
+	}
+}
+
+func apiErrCheck500(err error, w http.ResponseWriter, r *http.Request) {
+	if err != nil {
+		uip := getIPFromCtx(r.Context())
+		log.Printf("%v :: %v %v :: %v\n", uip, r.Method, r.URL, err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+	}
+}
+
+// apiUserQuery is called via apiEndpointHandler when
+// the endpoint is "users" and r.FormValue("q") is not empty.
+// It queries the registry cache for users or user URLs
+// matching the term supplied via r.FormValue("q")
+func apiEndpointQuery(w http.ResponseWriter, r *http.Request) {
+	query := r.FormValue("q")
+	urls := r.FormValue("url")
+	var out []string
+	var out2 []string
+	var err error
+
+	vars := mux.Vars(r)
+	endpoint := vars["endpoint"]
+
+	// Handle user URL queries first, then nickname queries.
+	// Concatenate both outputs if they're both set.
+	// Also handle mention queries and status queries.
+	// If we made it this far and 'default' is matched,
+	// something went very wrong.
+	switch endpoint {
+	case "users":
+		if urls != "" {
+			out2, err = twtxtCache.QueryUser(urls)
+			out = append(out, out2...)
+			apiErrCheck(err, r)
+		}
+		if query != "" {
+			out2, err = twtxtCache.QueryUser(query)
+			out = append(out, out2...)
+			apiErrCheck(err, r)
+		}
+
+	case "mentions":
+		out, err = twtxtCache.QueryInStatus(query)
+		apiErrCheck(err, r)
+
+	case "tweets":
+		out, err = twtxtCache.QueryInStatus(query)
+		apiErrCheck(err, r)
+
+	default:
+		http.Error(w, "500", http.StatusInternalServerError)
+	}
+
+	// iterate over the output. if there aren't
+	// explicit newlines, add them.
+	var data []byte
+	for _, e := range out {
+		data = append(data, []byte(e)...)
+		if !strings.HasSuffix(e, "\n") {
+			data = append(data, byte('\n'))
+		}
+	}
+
+	w.Header().Set("Content-Type", txtutf8)
+	_, err = w.Write(data)
+	apiErrCheck500(err, w, r)
+}