changeset 206:cd6ad5eaef8d

Renamed cmd/tokenserver to cmd/gemma.
author Sascha L. Teichmann <teichmann@intevation.de>
date Sun, 22 Jul 2018 10:40:17 +0200
parents 2a152816fc38
children 88d21c29cf04
files Makefile README.md client/package.json cmd/gemma/main.go cmd/gemma/token.go cmd/gemma/user.go cmd/tokenserver/main.go cmd/tokenserver/token.go cmd/tokenserver/user.go
diffstat 9 files changed, 341 insertions(+), 341 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sun Jul 22 10:24:28 2018 +0200
+++ b/Makefile	Sun Jul 22 10:40:17 2018 +0200
@@ -11,12 +11,12 @@
 
 export BUILDBASE
 
-tokenserver-bin := cmd/tokenserver/tokenserver
+gemma-bin := cmd/gemma/gemma
 3rdpartylibs-stamp := $(BUILDBASE)/3rdpartylibs-build
 
-.PHONY: all 3rdpartylibs tokenserver client clean
+.PHONY: all 3rdpartylibs gemma client clean
 
-all: tokenserver client
+all: gemma client
 
 $(ENVWARPPER):
 	@echo "Preparing go build environment:"
@@ -37,15 +37,15 @@
 
 3rdpartylibs: $(3rdpartylibs-stamp)
 
-$(tokenserver-bin): $(3rdpartylibs-stamp) $(ENVWARPPER)
-	cd cmd/tokenserver && "$(ENVWARPPER)" go build
+$(gemma-bin): $(3rdpartylibs-stamp) $(ENVWARPPER)
+	cd cmd/gemma && "$(ENVWARPPER)" go build
 
-tokenserver: $(tokenserver-bin)
+gemma: $(gemma-bin)
 
 client:
 	$(MAKE) -f Makefile.build -C client
 
 clean:
 	$(MAKE) -f Makefile.build -C client $@
-	rm "$(tokenserver-bin)"
+	rm "$(gemma-bin)"
 	rm -rf "$(BUILDBASE)"
--- a/README.md	Sun Jul 22 10:24:28 2018 +0200
+++ b/README.md	Sun Jul 22 10:40:17 2018 +0200
@@ -10,13 +10,13 @@
 
     Prerequesite: compile server
     ```
-    cd cmd/tokenserver/
+    cd cmd/gemma/
     go build
     cd ../../
     ```
 
 * Run
-    Run server with  `./cmd/tokenserver/tokenserver`
+    Run server with  `./cmd/gemma/gemma`
 
 
 ## Client
--- a/client/package.json	Sun Jul 22 10:24:28 2018 +0200
+++ b/client/package.json	Sun Jul 22 10:40:17 2018 +0200
@@ -3,7 +3,7 @@
   "version": "0.1.0",
   "private": true,
   "scripts": {
-    "run:both": "concurrently \"../cmd/tokenserver/tokenserver\" \"vue-cli-service serve\"",
+    "run:both": "concurrently \"../cmd/gemma/gemma\" \"vue-cli-service serve\"",
     "swagger": "serve ../api/server/ui",
     "serve": "vue-cli-service serve",
     "build": "vue-cli-service build",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmd/gemma/main.go	Sun Jul 22 10:40:17 2018 +0200
@@ -0,0 +1,69 @@
+package main
+
+import (
+	"context"
+	"flag"
+	"fmt"
+	"log"
+	"net/http"
+	"os"
+	"os/signal"
+	"path/filepath"
+	"syscall"
+
+	"gemma.intevation.de/gemma/auth"
+)
+
+func sysAdmin(rw http.ResponseWriter, req *http.Request) {
+	session, _ := auth.GetSession(req)
+	rw.Header().Set("Content-Type", "text/plain")
+	fmt.Fprintf(rw, "%s is a sys_admin\n", session.User)
+}
+
+func main() {
+	port := flag.Int("port", 8000, "port to listen at.")
+	host := flag.String("host", "localhost", "host to listen at.")
+	flag.Parse()
+	p, _ := filepath.Abs("./web")
+	mux := http.NewServeMux()
+	mux.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(p))))
+	mux.HandleFunc("/api/token", token)
+	mux.Handle("/api/logout", auth.SessionMiddleware(http.HandlerFunc(token)))
+	mux.Handle("/api/renew", auth.SessionMiddleware(http.HandlerFunc(renew)))
+	mux.Handle("/api/sys_admin",
+		auth.SessionMiddleware(
+			auth.SessionChecker(http.HandlerFunc(sysAdmin), auth.HasRole("sys_admin"))))
+	mux.Handle("/api/create_user",
+		auth.SessionMiddleware(
+			auth.SessionChecker(http.HandlerFunc(createUser), auth.HasRole("sys_admin"))))
+
+	addr := fmt.Sprintf("%s:%d", *host, *port)
+
+	server := http.Server{Addr: addr, Handler: mux}
+
+	done := make(chan error)
+
+	go func() {
+		defer close(done)
+		done <- server.ListenAndServe()
+	}()
+
+	sigChan := make(chan os.Signal)
+	signal.Notify(sigChan, os.Interrupt, os.Kill, syscall.SIGTERM)
+
+	select {
+	case err := <-done:
+		if err != nil && err != http.ErrServerClosed {
+			log.Fatalf("error: %v\n", err)
+		}
+	case <-sigChan:
+	}
+
+	server.Shutdown(context.Background())
+
+	<-done
+
+	if err := auth.ConnPool.Shutdown(); err != nil {
+		log.Fatalf("error: %v\n", err)
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmd/gemma/token.go	Sun Jul 22 10:40:17 2018 +0200
@@ -0,0 +1,82 @@
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"log"
+	"net/http"
+
+	"gemma.intevation.de/gemma/auth"
+)
+
+func renew(rw http.ResponseWriter, req *http.Request) {
+	token, _ := auth.GetToken(req)
+	newToken, err := auth.ConnPool.Renew(token)
+	switch {
+	case err == auth.ErrNoSuchToken:
+		http.NotFound(rw, req)
+		return
+	case err != nil:
+		http.Error(rw, fmt.Sprintf("error: %v", err), http.StatusInternalServerError)
+		return
+	}
+
+	session, _ := auth.GetSession(req)
+
+	var result = struct {
+		Token   string   `json:"token"`
+		Expires int64    `json:"expires"`
+		User    string   `json:"user"`
+		Roles   []string `json:"roles"`
+	}{
+		Token:   newToken,
+		Expires: session.ExpiresAt,
+		User:    session.User,
+		Roles:   session.Roles,
+	}
+
+	rw.Header().Set("Content-Type", "text/plain")
+	if err := json.NewEncoder(rw).Encode(&result); err != nil {
+		log.Printf("error: %v\n", err)
+	}
+}
+
+func logout(rw http.ResponseWriter, req *http.Request) {
+	token, _ := auth.GetToken(req)
+	deleted := auth.ConnPool.Delete(token)
+	if !deleted {
+		http.NotFound(rw, req)
+		return
+	}
+	rw.Header().Set("Content-Type", "text/plain")
+	fmt.Fprintln(rw, "token deleted")
+}
+
+func token(rw http.ResponseWriter, req *http.Request) {
+	user := req.FormValue("user")
+	password := req.FormValue("password")
+
+	token, session, err := auth.GenerateSession(user, password)
+
+	if err != nil {
+		http.Error(rw, fmt.Sprintf("error: %v", err), http.StatusInternalServerError)
+		return
+	}
+
+	var result = struct {
+		Token   string   `json:"token"`
+		Expires int64    `json:"expires"`
+		User    string   `json:"user"`
+		Roles   []string `json:"roles"`
+	}{
+		Token:   token,
+		Expires: session.ExpiresAt,
+		User:    session.User,
+		Roles:   session.Roles,
+	}
+
+	rw.Header().Set("Content-Type", "application/json")
+	if err := json.NewEncoder(rw).Encode(&result); err != nil {
+		log.Printf("error: %v\n", err)
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmd/gemma/user.go	Sun Jul 22 10:40:17 2018 +0200
@@ -0,0 +1,180 @@
+package main
+
+import (
+	"database/sql"
+	"encoding/json"
+	"errors"
+	"log"
+	"net/http"
+	"regexp"
+	"strings"
+
+	"gemma.intevation.de/gemma/auth"
+	"github.com/jackc/pgx"
+)
+
+type (
+	Email   string
+	Country string
+	Role    string
+
+	BoundingBox struct {
+		X1 float64 `json:"x1"`
+		Y1 float64 `json:"y1"`
+		X2 float64 `json:"x2"`
+		Y2 float64 `json:"y2"`
+	}
+
+	User struct {
+		User     string       `json:"user"`
+		Role     Role         `json:"role"`
+		Password string       `json:"password"`
+		Email    Email        `json:"email"`
+		Country  Country      `json:"country"`
+		Extent   *BoundingBox `json:"extent"`
+	}
+)
+
+const (
+	createUserSQL       = `SELECT create_user($1, $2, $3, $4, NULL, $5)`
+	createUserExtentSQL = `SELECT create_user($1, $2, $3, $4,
+  ST_MakeBox2D(ST_Point($5, $6), ST_Point($7, $8)), $9)`
+)
+
+var (
+	// https://stackoverflow.com/questions/201323/how-to-validate-an-email-address-using-a-regular-expression
+	emailRe = regexp.MustCompile(
+		`(?:[a-z0-9!#$%&'*+/=?^_` + "`" +
+			`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_` + "`" +
+			`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]` +
+			`|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")` +
+			`@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?` +
+			`|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}` +
+			`(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]` +
+			`:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]` +
+			`|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])`)
+	errNoEmailAddress = errors.New("Not a valid email address")
+)
+
+func (e *Email) UnmarshalJSON(data []byte) error {
+	var s string
+	if err := json.Unmarshal(data, &s); err != nil {
+		return err
+	}
+	if !emailRe.MatchString(s) {
+		return errNoEmailAddress
+	}
+	*e = Email(s)
+	return nil
+}
+
+var (
+	validCountries = []string{
+		"AT", "BG", "DE", "HU", "HR",
+		"MD", "RO", "RS", "SK", "UA",
+	}
+	errNoValidCountry = errors.New("Not a valid country")
+)
+
+func (c *Country) UnmarshalJSON(data []byte) error {
+	var s string
+	if err := json.Unmarshal(data, &s); err != nil {
+		return err
+	}
+	s = strings.ToUpper(s)
+	for _, v := range validCountries {
+		if v == s {
+			*c = Country(v)
+			return nil
+		}
+	}
+	return errNoValidCountry
+}
+
+var (
+	validRoles = []string{
+		"waterway_user",
+		"waterway_admin",
+		"sys_admin",
+	}
+	errNoValidRole = errors.New("Not a valid role")
+)
+
+func (r *Role) UnmarshalJSON(data []byte) error {
+	var s string
+	if err := json.Unmarshal(data, &s); err != nil {
+		return err
+	}
+	s = strings.ToLower(s)
+	for _, v := range validRoles {
+		if v == s {
+			*r = Role(v)
+			return nil
+		}
+	}
+	return errNoValidRole
+}
+
+func createUser(rw http.ResponseWriter, req *http.Request) {
+
+	var user User
+
+	defer req.Body.Close()
+	if err := json.NewDecoder(req.Body).Decode(&user); err != nil {
+		http.Error(rw, "error: "+err.Error(), http.StatusBadRequest)
+		return
+	}
+
+	token, _ := auth.GetToken(req)
+	err := auth.ConnPool.Do(token, func(db *sql.DB) (err error) {
+		if user.Extent == nil {
+			_, err = db.Exec(
+				createUserSQL,
+				string(user.Role),
+				user.User,
+				user.Password,
+				string(user.Country),
+				string(user.Email),
+			)
+		} else {
+			_, err = db.Exec(
+				createUserSQL,
+				string(user.Role),
+				user.User,
+				user.Password,
+				string(user.Country),
+				user.Extent.X1, user.Extent.Y1,
+				user.Extent.X2, user.Extent.Y2,
+				string(user.Email),
+			)
+		}
+		return
+	})
+
+	var res struct {
+		Result  string `json:"result"`
+		Code    string `json:"code,omitempty"`
+		Message string `json:"message,omitempty"`
+	}
+
+	if err != nil {
+		if pgErr, ok := err.(pgx.PgError); ok {
+			res.Result = "failure"
+			res.Code = pgErr.Code
+			res.Message = pgErr.Message
+		} else {
+			log.Printf("err: %v\n", err)
+			http.Error(rw,
+				"error: "+err.Error(),
+				http.StatusInternalServerError)
+			return
+		}
+	} else {
+		res.Result = "success"
+	}
+
+	rw.Header().Set("Content-Type", "application/json")
+	if err := json.NewEncoder(rw).Encode(&res); err != nil {
+		log.Printf("error: %v\n", err)
+	}
+}
--- a/cmd/tokenserver/main.go	Sun Jul 22 10:24:28 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-package main
-
-import (
-	"context"
-	"flag"
-	"fmt"
-	"log"
-	"net/http"
-	"os"
-	"os/signal"
-	"path/filepath"
-	"syscall"
-
-	"gemma.intevation.de/gemma/auth"
-)
-
-func sysAdmin(rw http.ResponseWriter, req *http.Request) {
-	session, _ := auth.GetSession(req)
-	rw.Header().Set("Content-Type", "text/plain")
-	fmt.Fprintf(rw, "%s is a sys_admin\n", session.User)
-}
-
-func main() {
-	port := flag.Int("port", 8000, "port to listen at.")
-	host := flag.String("host", "localhost", "host to listen at.")
-	flag.Parse()
-	p, _ := filepath.Abs("./web")
-	mux := http.NewServeMux()
-	mux.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(p))))
-	mux.HandleFunc("/api/token", token)
-	mux.Handle("/api/logout", auth.SessionMiddleware(http.HandlerFunc(token)))
-	mux.Handle("/api/renew", auth.SessionMiddleware(http.HandlerFunc(renew)))
-	mux.Handle("/api/sys_admin",
-		auth.SessionMiddleware(
-			auth.SessionChecker(http.HandlerFunc(sysAdmin), auth.HasRole("sys_admin"))))
-	mux.Handle("/api/create_user",
-		auth.SessionMiddleware(
-			auth.SessionChecker(http.HandlerFunc(createUser), auth.HasRole("sys_admin"))))
-
-	addr := fmt.Sprintf("%s:%d", *host, *port)
-
-	server := http.Server{Addr: addr, Handler: mux}
-
-	done := make(chan error)
-
-	go func() {
-		defer close(done)
-		done <- server.ListenAndServe()
-	}()
-
-	sigChan := make(chan os.Signal)
-	signal.Notify(sigChan, os.Interrupt, os.Kill, syscall.SIGTERM)
-
-	select {
-	case err := <-done:
-		if err != nil && err != http.ErrServerClosed {
-			log.Fatalf("error: %v\n", err)
-		}
-	case <-sigChan:
-	}
-
-	server.Shutdown(context.Background())
-
-	<-done
-
-	if err := auth.ConnPool.Shutdown(); err != nil {
-		log.Fatalf("error: %v\n", err)
-	}
-}
--- a/cmd/tokenserver/token.go	Sun Jul 22 10:24:28 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-package main
-
-import (
-	"encoding/json"
-	"fmt"
-	"log"
-	"net/http"
-
-	"gemma.intevation.de/gemma/auth"
-)
-
-func renew(rw http.ResponseWriter, req *http.Request) {
-	token, _ := auth.GetToken(req)
-	newToken, err := auth.ConnPool.Renew(token)
-	switch {
-	case err == auth.ErrNoSuchToken:
-		http.NotFound(rw, req)
-		return
-	case err != nil:
-		http.Error(rw, fmt.Sprintf("error: %v", err), http.StatusInternalServerError)
-		return
-	}
-
-	session, _ := auth.GetSession(req)
-
-	var result = struct {
-		Token   string   `json:"token"`
-		Expires int64    `json:"expires"`
-		User    string   `json:"user"`
-		Roles   []string `json:"roles"`
-	}{
-		Token:   newToken,
-		Expires: session.ExpiresAt,
-		User:    session.User,
-		Roles:   session.Roles,
-	}
-
-	rw.Header().Set("Content-Type", "text/plain")
-	if err := json.NewEncoder(rw).Encode(&result); err != nil {
-		log.Printf("error: %v\n", err)
-	}
-}
-
-func logout(rw http.ResponseWriter, req *http.Request) {
-	token, _ := auth.GetToken(req)
-	deleted := auth.ConnPool.Delete(token)
-	if !deleted {
-		http.NotFound(rw, req)
-		return
-	}
-	rw.Header().Set("Content-Type", "text/plain")
-	fmt.Fprintln(rw, "token deleted")
-}
-
-func token(rw http.ResponseWriter, req *http.Request) {
-	user := req.FormValue("user")
-	password := req.FormValue("password")
-
-	token, session, err := auth.GenerateSession(user, password)
-
-	if err != nil {
-		http.Error(rw, fmt.Sprintf("error: %v", err), http.StatusInternalServerError)
-		return
-	}
-
-	var result = struct {
-		Token   string   `json:"token"`
-		Expires int64    `json:"expires"`
-		User    string   `json:"user"`
-		Roles   []string `json:"roles"`
-	}{
-		Token:   token,
-		Expires: session.ExpiresAt,
-		User:    session.User,
-		Roles:   session.Roles,
-	}
-
-	rw.Header().Set("Content-Type", "application/json")
-	if err := json.NewEncoder(rw).Encode(&result); err != nil {
-		log.Printf("error: %v\n", err)
-	}
-}
--- a/cmd/tokenserver/user.go	Sun Jul 22 10:24:28 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-package main
-
-import (
-	"database/sql"
-	"encoding/json"
-	"errors"
-	"log"
-	"net/http"
-	"regexp"
-	"strings"
-
-	"gemma.intevation.de/gemma/auth"
-	"github.com/jackc/pgx"
-)
-
-type (
-	Email   string
-	Country string
-	Role    string
-
-	BoundingBox struct {
-		X1 float64 `json:"x1"`
-		Y1 float64 `json:"y1"`
-		X2 float64 `json:"x2"`
-		Y2 float64 `json:"y2"`
-	}
-
-	User struct {
-		User     string       `json:"user"`
-		Role     Role         `json:"role"`
-		Password string       `json:"password"`
-		Email    Email        `json:"email"`
-		Country  Country      `json:"country"`
-		Extent   *BoundingBox `json:"extent"`
-	}
-)
-
-const (
-	createUserSQL       = `SELECT create_user($1, $2, $3, $4, NULL, $5)`
-	createUserExtentSQL = `SELECT create_user($1, $2, $3, $4,
-  ST_MakeBox2D(ST_Point($5, $6), ST_Point($7, $8)), $9)`
-)
-
-var (
-	// https://stackoverflow.com/questions/201323/how-to-validate-an-email-address-using-a-regular-expression
-	emailRe = regexp.MustCompile(
-		`(?:[a-z0-9!#$%&'*+/=?^_` + "`" +
-			`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_` + "`" +
-			`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]` +
-			`|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")` +
-			`@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?` +
-			`|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}` +
-			`(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]` +
-			`:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]` +
-			`|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])`)
-	errNoEmailAddress = errors.New("Not a valid email address")
-)
-
-func (e *Email) UnmarshalJSON(data []byte) error {
-	var s string
-	if err := json.Unmarshal(data, &s); err != nil {
-		return err
-	}
-	if !emailRe.MatchString(s) {
-		return errNoEmailAddress
-	}
-	*e = Email(s)
-	return nil
-}
-
-var (
-	validCountries = []string{
-		"AT", "BG", "DE", "HU", "HR",
-		"MD", "RO", "RS", "SK", "UA",
-	}
-	errNoValidCountry = errors.New("Not a valid country")
-)
-
-func (c *Country) UnmarshalJSON(data []byte) error {
-	var s string
-	if err := json.Unmarshal(data, &s); err != nil {
-		return err
-	}
-	s = strings.ToUpper(s)
-	for _, v := range validCountries {
-		if v == s {
-			*c = Country(v)
-			return nil
-		}
-	}
-	return errNoValidCountry
-}
-
-var (
-	validRoles = []string{
-		"waterway_user",
-		"waterway_admin",
-		"sys_admin",
-	}
-	errNoValidRole = errors.New("Not a valid role")
-)
-
-func (r *Role) UnmarshalJSON(data []byte) error {
-	var s string
-	if err := json.Unmarshal(data, &s); err != nil {
-		return err
-	}
-	s = strings.ToLower(s)
-	for _, v := range validRoles {
-		if v == s {
-			*r = Role(v)
-			return nil
-		}
-	}
-	return errNoValidRole
-}
-
-func createUser(rw http.ResponseWriter, req *http.Request) {
-
-	var user User
-
-	defer req.Body.Close()
-	if err := json.NewDecoder(req.Body).Decode(&user); err != nil {
-		http.Error(rw, "error: "+err.Error(), http.StatusBadRequest)
-		return
-	}
-
-	token, _ := auth.GetToken(req)
-	err := auth.ConnPool.Do(token, func(db *sql.DB) (err error) {
-		if user.Extent == nil {
-			_, err = db.Exec(
-				createUserSQL,
-				string(user.Role),
-				user.User,
-				user.Password,
-				string(user.Country),
-				string(user.Email),
-			)
-		} else {
-			_, err = db.Exec(
-				createUserSQL,
-				string(user.Role),
-				user.User,
-				user.Password,
-				string(user.Country),
-				user.Extent.X1, user.Extent.Y1,
-				user.Extent.X2, user.Extent.Y2,
-				string(user.Email),
-			)
-		}
-		return
-	})
-
-	var res struct {
-		Result  string `json:"result"`
-		Code    string `json:"code,omitempty"`
-		Message string `json:"message,omitempty"`
-	}
-
-	if err != nil {
-		if pgErr, ok := err.(pgx.PgError); ok {
-			res.Result = "failure"
-			res.Code = pgErr.Code
-			res.Message = pgErr.Message
-		} else {
-			log.Printf("err: %v\n", err)
-			http.Error(rw,
-				"error: "+err.Error(),
-				http.StatusInternalServerError)
-			return
-		}
-	} else {
-		res.Result = "success"
-	}
-
-	rw.Header().Set("Content-Type", "application/json")
-	if err := json.NewEncoder(rw).Encode(&res); err != nil {
-		log.Printf("error: %v\n", err)
-	}
-}