Jelajahi Sumber

db related torrent downloader

TooManySugar 3 tahun lalu
induk
melakukan
f78e2694de
5 mengubah file dengan 283 tambahan dan 188 penghapusan
  1. 3 0
      .gitignore
  2. 137 80
      cmd/web/connector.go
  3. 87 52
      cmd/web/handlers.go
  4. 48 56
      cmd/web/main.go
  5. 8 0
      go.sum

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+logs
+config.json
+files/torrents/*

+ 137 - 80
cmd/web/connector.go

@@ -1,74 +1,83 @@
-
 package main
 
 import (
+	"database/sql"
+	"encoding/json"
 	"fmt"
 	"io/ioutil"
-	"encoding/json"
-	"database/sql"
+	"log"
+
 	_ "github.com/go-sql-driver/mysql"
 )
 
 type ConnectionProperties struct {
-	User 	string
-	Pass 	string
-	Host	string
-	Db 		string
+	User string
+	Pass string
+	Host string
+	Db   string
 }
 
 type connection struct {
-	dBOpenStr string
+	dBOpenStr      string
 	connProperties ConnectionProperties
 }
 
-func (c *connection) Init(filepath string){
-	if (NOMYSQL) {
+type Torrent struct {
+	ID      int64
+	Hash    string
+	Name    string
+	OwnerID string
+	Time    string
+	Deleted bool
+}
+
+func (c *connection) Init(filepath string) {
+	if NOMYSQL {
 		return
 	}
 	b, err := ioutil.ReadFile(filepath)
-    if err != nil {
-      fmt.Print(err)
-    }
-    propJson := string(b)
+	if err != nil {
+		fmt.Print(err)
+	}
+	propJson := string(b)
 
 	json.Unmarshal([]byte(propJson), &c.connProperties)
-	
+
 	//c.connProperties.Db = "nosite"
 	fmt.Printf("Connection data:\n%s\n%s\n%s\n%s\n", c.connProperties.User, c.connProperties.Pass, c.connProperties.Host, c.connProperties.Db)
-	c.dBOpenStr = fmt.Sprintf ("%s:%s@tcp(%s)/%s", c.connProperties.User, c.connProperties.Pass, c.connProperties.Host, c.connProperties.Db)
+	c.dBOpenStr = fmt.Sprintf("%s:%s@tcp(%s)/%s", c.connProperties.User, c.connProperties.Pass, c.connProperties.Host, c.connProperties.Db)
 	fmt.Printf("Connecting with:\n%s\n", c.dBOpenStr)
 	db, err := sql.Open("mysql", c.dBOpenStr)
 	if err != nil {
 		panic(err)
 	}
 	if err = db.Ping(); err != nil {
-        db.Close()
-        logger.Print("Fatal : Error with connection to database!")
-    } else {
-    	fmt.Print("Connection succesfull!")
-    	return
-    }
-    
-
-    logger.Print("Trying to connect to DB-server...")
-    c.dBOpenStr = fmt.Sprintf ("%s:%s@tcp(%s)/", c.connProperties.User, c.connProperties.Pass, c.connProperties.Host)
-    fmt.Printf("Connecting with:\n%s\n", c.dBOpenStr)
+		db.Close()
+		logger.Println("Fatal : Error with connection to database!")
+	} else {
+		fmt.Println("Connection succesfull!")
+		return
+	}
+
+	logger.Print("Trying to connect to DB-server...")
+	c.dBOpenStr = fmt.Sprintf("%s:%s@tcp(%s)/", c.connProperties.User, c.connProperties.Pass, c.connProperties.Host)
+	fmt.Printf("Connecting with:\n%s\n", c.dBOpenStr)
 	db, err = sql.Open("mysql", c.dBOpenStr)
 	if err != nil {
 		panic(err)
 	}
 	if err = db.Ping(); err != nil {
-        db.Close()
-        logger.Print("Fatal : Error with connection to database server!")
-        return
-    } else {
-    	
-    }
-    c.databaseInitialization();
+		db.Close()
+		logger.Print("Fatal : Error with connection to database server!")
+		return
+	} else {
+
+	}
+	c.databaseInitialization()
 }
 
 /*  создает на сервере необходимую бд и таблицы  */
-func (c *connection) databaseInitialization() { 
+func (c *connection) databaseInitialization() {
 	db, err := sql.Open("mysql", c.dBOpenStr)
 
 	if err != nil {
@@ -83,46 +92,60 @@ func (c *connection) databaseInitialization() {
 	act_query = "SELECT count(*) FROM information_schema.tables WHERE TABLE_SCHEMA = '" + c.connProperties.Db + "';"
 	db.QueryRow(act_query).Scan(&counter)
 	fmt.Print(counter)
-	if (counter != 0) {
+	if counter != 0 {
 		logger.Print("Server already has the specified database")
 	} else {
 		logger.Print("The server does not have the specified database")
 		logger.Printf("Creating database '%s'...", c.connProperties.Db)
 		act_query = "CREATE SCHEMA " + c.connProperties.Db + " DEFAULT CHARACTER SET utf8 ;"
 		result, err := db.Exec(act_query)
-	    if err != nil{
-	        panic(err)
-	    }
+		if err != nil {
+			panic(err)
+		}
 
-	    rowsCount, _ := result.RowsAffected()
+		rowsCount, _ := result.RowsAffected()
 		fmt.Printf("Lines changed: %d\n", rowsCount)
-	    if (rowsCount == 1) {
-	    	logger.Print("Succesfull!")
-	    }
+		if rowsCount == 1 {
+			logger.Print("Succesfull!")
+		}
 	}
 
 	logger.Print("Checking for existence of table 'users' on server...")
-	act_query = "SELECT count(*) FROM information_schema.tables WHERE TABLE_NAME = 'users' AND TABLE_SCHEMA = '"+ c.connProperties.Db +"';"
+	act_query = "SELECT count(*) FROM information_schema.tables WHERE TABLE_NAME = 'users' AND TABLE_SCHEMA = '" + c.connProperties.Db + "';"
 	db.QueryRow(act_query).Scan(&counter)
 	fmt.Print(counter)
-	if (counter != 0) {
+	if counter != 0 {
 		logger.Print("Server already has the specified table!")
 	} else {
 		logger.Print("The server does not have the specified table")
-		logger.Printf("Creating table '%s'.'users'...",c.connProperties.Db)
+		logger.Printf("Creating table '%s'.'users'...", c.connProperties.Db)
 		act_query = "CREATE TABLE `" + c.connProperties.Db + "`.`users` ( `idusers` INT NOT NULL AUTO_INCREMENT,`username` VARCHAR(45) NOT NULL, `password` VARCHAR(45) NOT NULL, PRIMARY KEY(`idusers`), UNIQUE INDEX `idusers_UNIQUE` (`idusers` ASC), UNIQUE INDEX `username_UNIQUE` (`username` ASC)) ENGINE = InnoDB DEFAULT CHARACTER SET utf8 ;"
 		fmt.Print(act_query)
 		_, err := db.Exec(act_query)
-	    if err != nil{
-	        panic(err)
-	    }
-    	logger.Print("Succesfull!")
+		if err != nil {
+			panic(err)
+		}
+		logger.Print("Succesfull!")
 	}
+	/*
+
+		CREATE TABLE `gosite`.`torrents` (
+		`id` INT NOT NULL AUTO_INCREMENT,
+		`hash` VARCHAR(45) NOT NULL,
+		`name` VARCHAR(45) NOT NULL,
+		`ownerid` VARCHAR(45) NOT NULL,
+		`time` TIMESTAMP NOT NULL,
+		`deleted` TINYINT NULL,
+		UNIQUE INDEX `idtorrents_UNIQUE` (`id` ASC) VISIBLE,
+		PRIMARY KEY (`hash`),
+		UNIQUE INDEX `hash_UNIQUE` (`hash` ASC) VISIBLE);
 
 
+	*/
+
 }
 
-func (c connection) LogIn(username string, password string) bool{
+func (c connection) LogIn(username string, password string) bool {
 	fmt.Printf("\n\nLogIn\nConnecting with:\n%s\n", c.dBOpenStr)
 	db, err := sql.Open("mysql", c.dBOpenStr)
 
@@ -137,12 +160,11 @@ func (c connection) LogIn(username string, password string) bool{
 	act_query := fmt.Sprintf("SELECT count(*) FROM %s.users WHERE username='%s' AND password=SHA('%s');", c.connProperties.Db, username, password)
 	db.QueryRow(act_query).Scan(&counter)
 	fmt.Println("we have", counter, "rows")
-    
-    if (counter == 0) {
-    	return false
-    }
-	return true
 
+	if counter == 0 {
+		return false
+	}
+	return true
 
 }
 
@@ -157,40 +179,75 @@ func (c connection) IsNameUsed(username string) bool {
 	var counter int
 	act_query := fmt.Sprintf("SELECT count(*) FROM %s.users WHERE username='%s';", c.connProperties.Db, username)
 	db.QueryRow(act_query).Scan(&counter)
-    
-	if (counter == 0) {
+
+	if counter == 0 {
 		fmt.Printf("Username unused\n")
-    	return false
-    }
+		return false
+	}
 	fmt.Printf("Username used\n")
 	return true
 }
 
 func (c connection) SigInUser(username string, password string) bool {
-	db, err := sql.Open("mysql", c.dBOpenStr)  
-    if err != nil {
-        panic(err)
-    } 
-    defer db.Close()
-    act_query := fmt.Sprintf("INSERT INTO %s.users (username, password) VALUES ('%s', SHA('%s'))", c.connProperties.Db, username, password)
-    result, err := db.Exec(act_query)
-    if err != nil{
-        panic(err)
-    }
-
-    rowsCount, _ := result.RowsAffected()
-    if (rowsCount == 1) {
-	    fmt.Printf("Lines changed: %d\n", rowsCount)
-	    return true
-    } else {
-    	return false
-    }
+	db, err := sql.Open("mysql", c.dBOpenStr)
+	if err != nil {
+		panic(err)
+	}
+	defer db.Close()
+	act_query := fmt.Sprintf("INSERT INTO %s.users (username, password) VALUES ('%s', SHA('%s'))", c.connProperties.Db, username, password)
+	result, err := db.Exec(act_query)
+	if err != nil {
+		panic(err)
+	}
+
+	rowsCount, _ := result.RowsAffected()
+	if rowsCount == 1 {
+		fmt.Printf("Lines changed: %d\n", rowsCount)
+		return true
+	} else {
+		return false
+	}
 }
 
-func (c connection) SubmitScore(score int){
+func (c connection) SubmitScore(score int) {
 	fmt.Printf("Submiting score %d", score)
 }
 
+func (c connection) TakeTorrent(hash string) (Torrent, error) {
+	db, err := sql.Open("mysql", c.dBOpenStr)
+	if err != nil {
+		panic(err)
+	}
+	defer db.Close()
+	act_query := fmt.Sprintf("SELECT * FROM `%s`.`torrents` WHERE `hash`='%s'", c.connProperties.Db, hash)
+	fmt.Println(act_query)
+	rows, err := db.Query(act_query)
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+
+	var torrent Torrent
+	for rows.Next() {
+
+		if err := rows.Scan(
+			&torrent.ID,
+			&torrent.Hash,
+			&torrent.Name,
+			&torrent.OwnerID,
+			&torrent.Time,
+			&torrent.Deleted,
+		); err != nil {
+			log.Fatal(err)
+		}
+
+		fmt.Println(torrent)
+		return torrent, nil
+	}
+
+	return torrent, fmt.Errorf("Torrent not found")
+}
+
 /*
 func main() {
 
@@ -209,4 +266,4 @@ func main() {
 		fmt.Printf("logIn error\n")
 	}
 }
-*/
+*/

+ 87 - 52
cmd/web/handlers.go

@@ -1,19 +1,24 @@
 package main
 
 import (
-	//"html/template"
-	"net/http"
 	"fmt"
+	"html/template"
+
+	// "io"
+	"net/http"
 	"strconv"
+
+	"github.com/gorilla/mux"
 )
 
 func indexHandler(w http.ResponseWriter, r *http.Request) {
 	data := struct {
-		User string
-		Title string
-		Items []string
+		User     string
+		Password string
+		Title    string
+		Items    []string
 	}{
-		User: "",
+		User:  "",
 		Title: "My page",
 		Items: []string{
 			"My photos",
@@ -21,35 +26,43 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
 			"More",
 		},
 	}
-	
 
 	session, _ := sessionsStore.Get(r, "session")
 
-	var (
-		password string
-		ok3 bool
-	)
-
 	untyped, ok := session.Values["username"]
 	if ok {
 		username, ok1 := untyped.(string)
 		if ok1 {
 			data.User = username
-			untyped, ok2 := session.Values["password"]
-			if ok2 {
-				password, ok3 = untyped.(string)
-				
-			} 
 		}
-	} 
-	
+	}
+
+	untyped, ok2 := session.Values["password"]
+	if ok2 {
+		password, ok3 := untyped.(string)
+		if ok3 {
+			data.Password = password
+		}
+	}
 
-	indexTemplate.Execute(w, data)
+	/*
+		fmt.Println(data.User)
+		fmt.Println(data.Password)
+	*/
 
-	if ok3 {
-		w.Write([]byte(password))
+	tindexTemplate, err := template.New("").ParseFiles(INDEX_TEMPLATE, "ui\\templates\\placeholder.html")
+
+	if err != nil {
+		fmt.Println("Error in templates loading")
+		fmt.Println(err)
+		w.Write([]byte("Internal server error"))
+		return
 	}
 
+	err = tindexTemplate.ExecuteTemplate(w, "index", data)
+	if err != nil {
+		fmt.Println(err)
+	}
 }
 
 func indexPostHandler(w http.ResponseWriter, r *http.Request) {
@@ -60,11 +73,11 @@ func logInGetHandler(w http.ResponseWriter, r *http.Request) {
 	data := struct {
 		Username string
 		Password string
-		Error string
+		Error    string
 	}{
 		Username: "",
 		Password: "",
-		Error: "",
+		Error:    "",
 	}
 	logInTemplate.Execute(w, data)
 }
@@ -74,37 +87,37 @@ func logInPostHandler(w http.ResponseWriter, r *http.Request) {
 	username := r.PostForm.Get("username")
 	password := r.PostForm.Get("password")
 	fmt.Printf("Post from website! r.PostFrom = %v\n", r.PostForm)
-	
-	if (dBConnector.LogIn(username, password)){
+
+	if dBConnector.LogIn(username, password) {
 
 		session, _ := sessionsStore.Get(r, "session")
 		session.Values["username"] = username
 		session.Values["password"] = password
 
-		session.Save(r,w)
+		session.Save(r, w)
 		http.Redirect(w, r, "/", http.StatusFound)
 	} else {
 		data := struct {
 			Username string
 			Password string
-			Error string
+			Error    string
 		}{
 			Username: username,
 			Password: password,
-			Error: "Неверный логин либо пароль",
+			Error:    "Неверный логин либо пароль",
 		}
 		fmt.Printf("Login error")
 		logInTemplate.Execute(w, data)
 		//w.Write([]byte("Login error"))
 	}
-	
+
 }
 
 func logOutGetHandler(w http.ResponseWriter, r *http.Request) {
 	session, _ := sessionsStore.Get(r, "session")
 	session.Options.MaxAge = -1
 	session.Save(r, w)
-	
+
 	untyped, ok := session.Values["username"]
 	if ok {
 		username, ok1 := untyped.(string)
@@ -127,29 +140,29 @@ func signInPostHandler(w http.ResponseWriter, r *http.Request) {
 	passwordRepeat := r.PostForm.Get("password-repeat")
 
 	data := struct {
-		Username string
-		Password string
+		Username       string
+		Password       string
 		PasswordRepeat string
-		Error string
+		Error          string
 	}{
-		Username: username,
-		Password: password,
+		Username:       username,
+		Password:       password,
 		PasswordRepeat: passwordRepeat,
-		Error: "",
+		Error:          "",
 	}
 
 	if dBConnector.IsNameUsed(username) {
 		data.Error = "Имя пользователя занято"
 	} else {
-		if (password != passwordRepeat) {
+		if password != passwordRepeat {
 			data.Error = "Введенные пароли не совпадают"
 		} else {
-			if dBConnector.SigInUser(username,password) {
-				
+			if dBConnector.SigInUser(username, password) {
+
 				session, _ := sessionsStore.Get(r, "session")
 				session.Values["username"] = username
 				session.Values["password"] = password
-				session.Save(r,w)
+				session.Save(r, w)
 
 				http.Redirect(w, r, "/", http.StatusFound)
 			} else {
@@ -164,22 +177,44 @@ func gameGetHandler(w http.ResponseWriter, r *http.Request) {
 	gameTemplate.Execute(w, nil)
 }
 
-func gamePostHandler(w http.ResponseWriter, r *http.Request){ //TODO запись score в таблицу
+func gamePostHandler(w http.ResponseWriter, r *http.Request) { //TODO запись score в таблицу
 	if err := r.ParseForm(); err != nil {
 		fmt.Fprintf(w, "ParseForm() err: %v", err)
 		return
 	}
 	fmt.Printf("Post from website! r.PostFrom = %v\n", r.PostForm)
 	score_str := r.FormValue("score")
-	fmt.Fprintf(w,"score = %s\n", score_str)
+	fmt.Fprintf(w, "score = %s\n", score_str)
 	fmt.Printf("score = %s\n", score_str)
 	score_int, err := strconv.Atoi(score_str)
-    if err != nil {
-        // handle error
-        fmt.Println(err)
-        logger.Printf("Error extracting score from '%s'",score_str)
-        //os.Exit(2)
-    } else {
-    	dBConnector.SubmitScore(score_int)
-    }
-}
+	if err != nil {
+		// handle error
+		fmt.Println(err)
+		logger.Printf("Error extracting score from '%s'", score_str)
+		// os.Exit(2)
+	} else {
+		dBConnector.SubmitScore(score_int)
+	}
+}
+
+func DownloadTorrentHandler(w http.ResponseWriter, r *http.Request) {
+	torrentHash := mux.Vars(r)["hash"]
+	fmt.Println(r.URL)
+
+	torrent, err := dBConnector.TakeTorrent(torrentHash)
+	if err != nil {
+		fmt.Println(err)
+		w.WriteHeader(http.StatusOK)
+		w.Write([]byte("Torrent file not found"))
+		return
+	}
+
+	if torrent.Deleted {
+		w.WriteHeader(http.StatusOK)
+		w.Write([]byte("The torrent file you are looking for has been deleted"))
+		return
+	}
+
+	w.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.torrent\"", torrent.Name))
+	http.ServeFile(w, r, fmt.Sprintf("files/torrents/%s.torrent", torrent.Hash))
+}

+ 48 - 56
cmd/web/main.go

@@ -1,103 +1,95 @@
-
-package main 
+package main
 
 import (
+	"fmt"
+	"html/template"
 	"log"
-	
+	"net/http"
 	"os"
 	"time"
-	"fmt"
-	"net/http"
-	"html/template"
+
 	"github.com/gorilla/mux"
 	"github.com/gorilla/sessions"
-
-
 )
 
 const (
-	NOMYSQL = false;
+	NOMYSQL        = false
+	INDEX_TEMPLATE = "ui\\templates\\index.html" //TODO в var структуру с парсом из файла JSON
 )
 
 var (
-
-
-	logger	*log.Logger
+	logger      *log.Logger
 	dBConnector connection
 
 	sessionsStore = sessions.NewCookieStore([]byte("mysecretcookie"))
 
-	indexTemplate = template.Must(template.ParseFiles("ui\\templates\\index.html"))
-	logInTemplate = template.Must(template.ParseFiles("ui\\templates\\login.html"))
+	logInTemplate  = template.Must(template.ParseFiles("ui\\templates\\login.html"))
 	signInTemplate = template.Must(template.ParseFiles("ui\\templates\\signin.html"))
-	gameTemplate = template.Must(template.ParseFiles("ui\\templates\\game.html"))
-)
-
+	gameTemplate   = template.Must(template.ParseFiles("ui\\templates\\game.html"))
 
+	requestRouter *mux.Router
+)
 
 func createLogger() {
 	startTime := time.Now()
 	logFileName := "logs/go-site_log_" + startTime.Format("2006-01-02_15-04-05") + ".txt"
 	file, err := os.OpenFile(logFileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
 	logger = log.New(file, "", log.Ldate|log.Ltime)
-  	
-  	if err != nil {
-    	log.Fatal(err)
+
+	if err != nil {
+		log.Fatal(err)
 	}
 
 }
 
-
 type justFilesFilesystem struct {
-    fs http.FileSystem
+	fs http.FileSystem
 }
 
 func (fs justFilesFilesystem) Open(name string) (http.File, error) {
-    f, err := fs.fs.Open(name)
-    if err != nil {
-        return nil, err
-    }
-    return neuteredReaddirFile{f}, nil
+	f, err := fs.fs.Open(name)
+	if err != nil {
+		return nil, err
+	}
+	return neuteredReaddirFile{f}, nil
 }
 
 type neuteredReaddirFile struct {
-    http.File
+	http.File
 }
 
 func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
-    return nil, nil
+	return nil, nil
 }
 
-
 func main() {
-	
-	
 
 	createLogger()
 
 	logger.Print("Hello, log file!")
 
-	requestRouter := mux.NewRouter()
-
-	
-    dBConnector.Init("config.json")
-    
-
-    requestRouter.HandleFunc("/", indexHandler).Methods("GET")
-    requestRouter.HandleFunc("/", indexPostHandler).Methods("POST")		//Есть ли нужда в обработке POST для /
-    requestRouter.HandleFunc("/login/", logInGetHandler).Methods("GET")
-    requestRouter.HandleFunc("/login/", logInPostHandler).Methods("POST")
-    requestRouter.HandleFunc("/logout/", logOutGetHandler).Methods("GET")
-    requestRouter.HandleFunc("/signin/", signInGetHandler).Methods("GET")
-    requestRouter.HandleFunc("/signin/", signInPostHandler).Methods("POST")
-    requestRouter.HandleFunc("/game/", gameGetHandler).Methods("GET")
-    requestRouter.HandleFunc("/game/", gamePostHandler).Methods("POST")
-    
-    fs := justFilesFilesystem{http.Dir("resources/")}
-    http.Handle("/resources/", http.StripPrefix("/resources/", http.FileServer(fs)))
-
-    http.Handle("/", requestRouter)
-
-    fmt.Print("Starting web-listener")
-    logger.Fatal(http.ListenAndServe(":8080", nil))
-}
+	requestRouter = mux.NewRouter()
+
+	dBConnector.Init("config.json")
+
+	requestRouter.HandleFunc("/", indexHandler).Methods("GET")
+	requestRouter.HandleFunc("/", indexPostHandler).Methods("POST") //Есть ли нужда в обработке POST для /
+	requestRouter.HandleFunc("/login/", logInGetHandler).Methods("GET")
+	requestRouter.HandleFunc("/login/", logInPostHandler).Methods("POST")
+	requestRouter.HandleFunc("/logout/", logOutGetHandler).Methods("GET")
+	requestRouter.HandleFunc("/signin/", signInGetHandler).Methods("GET")
+	requestRouter.HandleFunc("/signin/", signInPostHandler).Methods("POST")
+	requestRouter.HandleFunc("/game/", gameGetHandler).Methods("GET")
+	requestRouter.HandleFunc("/game/", gamePostHandler).Methods("POST")
+
+	requestRouter.HandleFunc("/files/torrents/{hash}.torrent", DownloadTorrentHandler).Methods("GET")
+
+	fs := justFilesFilesystem{http.Dir("resources/")}
+	http.Handle("/resources/", http.StripPrefix("/resources/", http.FileServer(fs)))
+
+	http.Handle("/", requestRouter)
+
+	fmt.Println("Starting web-listener")
+
+	logger.Fatal(http.ListenAndServe(":8080", nil))
+}

+ 8 - 0
go.sum

@@ -0,0 +1,8 @@
+github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
+github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
+github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
+github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
+github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
+github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=