Global Config Close #3

master
kreativmonkey 7 years ago
parent 9c36dd2732
commit b981f8b71c

@ -0,0 +1,18 @@
package main
import (
"net/http"
)
type response struct {
Action string `json:"action"`
Status string `json:"status_code"`
Result interface{} `json:"result"`
}
// GET: http://example.com/api/action/shorten?key=API_KEY_HERE&url=https://google.com&custom_ending=CUSTOM_ENDING
// Response: {"action": "shorten","result": "https://example.com/5kq"}
func APIRequest(w http.ResponseWriter, r *http.Request) {
}

@ -0,0 +1,64 @@
package main
import (
"flag"
"github.com/caarlos0/env"
"os"
)
func DefaultConfig() Config {
return Config{
DB: "./test.db",
APIkey: "thisIsNotASecretTokenNow",
Host: "localhost",
Port: "6889",
Title: "Sgot",
}
}
type Config struct {
DB string `env:"SHRT_DB_FILE"`
APIkey string `env:"SHRT_API_KEY"`
Host string `env:"SHRT_HOST"`
Port string `env:"SHRT_PORT"`
Domain string `env:"SHRT_DOMAIN"`
Title string `env:"SHRT_TITLE"`
}
func (c Config) HostPort() string {
return c.Host + ":" + c.Port
}
func ReadConfig() *Config {
c, err := readConfig(flag.NewFlagSet(os.Args[0], flag.ExitOnError), os.Args[1:])
if err != nil {
// sould never happen, because of flag default policy ExitOnError
panic(err)
}
return c
}
func readConfig(f *flag.FlagSet, args []string) (*Config, error){
config := DefaultConfig()
// Environment variables
err := env.Parse(&config)
if err != nil {
return nil, err
}
f.StringVar(&config.Host, "host", config.Host, "The host to listen on")
f.StringVar(&config.Port, "port", config.Port, "The port to listen on")
f.StringVar(&config.APIkey, "apikey", config.APIkey, "The Key to connect to the API")
f.StringVar(&config.DB, "db-file", config.DB, "The db file to use")
f.StringVar(&config.Domain, "domain", config.Domain, "The domain for redirect links")
f.StringVar(&config.Title, "title", config.Title, "The title on the Front")
// Arguments variables
err = f.Parse(args)
if err != nil {
return nil, err
}
return &config, err
}

@ -0,0 +1,69 @@
package main
import (
"testing"
"os"
"github.com/stretchr/testify/assert"
"flag"
)
func TestConfig_ReadConfigDefaults(t *testing.T){
originalArgs := os.Args
os.Args = []string{"shrt"}
defer func(){ os.Args = originalArgs }()
d := DefaultConfig()
assert.Equal(t, &d, ReadConfig())
}
func TestConfig_ReadConfig(t *testing.T){
input := []string{
"--host=host",
"--port=port",
"--db-file=db",
"--title=title",
"--apikey=apikey",
"--domain=domain",
}
expected := &Config{
Host: "host",
Port: "port",
DB: "db",
Title: "title",
APIkey: "apikey",
Domain: "domain",
}
cfg, err := readConfig(flag.NewFlagSet("", flag.ContinueOnError), input)
assert.NoError(t, err)
assert.Equal(t, expected, cfg)
}
func TestConfig_ReadConfigFromEnv(t *testing.T) {
assert.NoError(t, os.Setenv("SHRT_DB_FILE", "db"))
defer os.Unsetenv("SHRT_DB_FILE")
assert.NoError(t, os.Setenv("SHRT_API_KEY", "apikey"))
defer os.Unsetenv("SHRT_API_KEY")
assert.NoError(t, os.Setenv("SHRT_HOST", "host"))
defer os.Unsetenv("SHRT_HOST")
assert.NoError(t, os.Setenv("SHRT_PORT", "port"))
defer os.Unsetenv("SHRT_PORT")
assert.NoError(t, os.Setenv("SHRT_DOMAIN", "domain"))
defer os.Unsetenv("SHRT_DOMAIN")
assert.NoError(t, os.Setenv("SHRT_TITLE", "title"))
defer os.Unsetenv("SHRT_TITLE")
expected := &Config{
Host: "host",
Port: "port",
DB: "db",
Title: "title",
APIkey: "apikey",
Domain: "domain",
}
cfg, err := readConfig(flag.NewFlagSet("", flag.ContinueOnError), []string{})
assert.NoError(t, err)
assert.Equal(t, expected, cfg)
}

@ -6,7 +6,7 @@ import (
"fmt" "fmt"
"text/template" "text/template"
"net/url" "net/url"
shorty "github.com/kreativmonkey/shrt/short" "github.com/kreativmonkey/shrt/shrty"
) )
type Page struct { type Page struct {
@ -15,15 +15,13 @@ type Page struct {
Body string Body string
} }
type response struct {
Action string `json:"action"`
Result interface{} `json:"result"`
}
// Load the main Page with an Imput field for the URL to short. // Load the main Page with an Imput field for the URL to short.
func index(w http.ResponseWriter, r *http.Request) { func index(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("template/index.gohtml") t, _ := template.ParseFiles("template/index.gohtml")
t.Execute(w, Page{Title: "Shrt"}) t.Execute(w, Page{
Host: config.Host,
Title: config.Title,
})
} }
// Redirect to the URL behind the requested token. // Redirect to the URL behind the requested token.
@ -38,8 +36,8 @@ func redirect(w http.ResponseWriter, r *http.Request){
// //
func shorten(w http.ResponseWriter, r *http.Request){ func shorten(w http.ResponseWriter, r *http.Request){
var test string var token string
token, err := short.Add(r.FormValue("url"), &test) err := short.Short(r.FormValue("url"), &token)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -55,13 +53,13 @@ func shortenJSON(w http.ResponseWriter, r *http.Request){
queryu, err := url.QueryUnescape(query.Get("url")) queryu, err := url.QueryUnescape(query.Get("url"))
check(err) check(err)
var test string var token string
err = short.Short(queryu, &test) err = short.Short(queryu, &token)
check(err) check(err)
respond := response{ respond := response{3
Action: "shorten", Action: "shorten",
Result: r.Host + "/" + test, Result: r.Host + "/" + token,
} }
w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.Header().Set("Content-Type", "application/json; charset=UTF-8")
@ -79,7 +77,7 @@ func lookupJSON(w http.ResponseWriter, r *http.Request){
queryu, err := url.QueryUnescape(query.Get("url_ending")) queryu, err := url.QueryUnescape(query.Get("url_ending"))
check(err) check(err)
var result shorty.Shrt var result shrty.Data
var respond response var respond response
if ok := short.GetAPI(queryu, &result); ok { if ok := short.GetAPI(queryu, &result); ok {
respond := response{ respond := response{

@ -3,23 +3,23 @@ package main
import ( import (
"net/http" "net/http"
//"github.com/spf13/viper" shrty "github.com/kreativmonkey/shrt/shrty"
shorty "github.com/kreativmonkey/shrt/short"
"log" "log"
"fmt" "fmt"
) )
const version = "0.02" const version = "0.02"
const port = "9090"
const db = "./test.db"
var ( var (
short *shorty.Storage short *shrty.Storage
// Global configuration Variable
config = ReadConfig()
) )
func init() { func init() {
var err error var err error
short, err = shorty.Open(db) short, err = shrty.Open(config.DB)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -56,10 +56,9 @@ func main() {
http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("template/css")))) http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("template/css"))))
http.Handle("/img/", http.StripPrefix("/img/", http.FileServer(http.Dir("template/img")))) http.Handle("/img/", http.StripPrefix("/img/", http.FileServer(http.Dir("template/img"))))
*/ */
fmt.Printf("Shrt %s started on Port: %s \n", version, port) fmt.Printf("Shrty %s is listen on Port: %s \n", version, config.Port)
fmt.Printf("For the API use following key: %s \n", version, config.APIkey)
http.Handle("/", router) http.Handle("/", router)
log.Fatal(http.ListenAndServe(":"+port, router)) log.Fatal(http.ListenAndServe(":"+ config.Port, router))
} }

@ -1,4 +1,4 @@
package shrt package shrty
import ( import (
"bytes" "bytes"
@ -13,13 +13,20 @@ import (
) )
type Storage struct { type Storage struct {
Token map[string]*Shrt `json:"token"` Token map[string]*Data `json:"token"`
Url map[string]string `json:"url"` Url map[string]string `json:"url"`
} }
type Shrt struct { type Data struct {
URL string `json:"url"` URL string `json:"url"`
Date string `json:"datum"` URLFetched string `json:"url_fetched"`
CanonicalURL string `json:"canonical_url"`
OriginalURL string `json:"original_url"`
Domain string `json:"domain"`
FavIconLink string `json:"favicon_url"`
HTTPStatusCode string `json:"http_code"`
Category string `json:"category"`
Created string `json:"created_at"`
Clicks int64 `json:"clicks"` Clicks int64 `json:"clicks"`
} }
@ -31,7 +38,7 @@ var (
// Open up // Open up
func Open(path string) (*Storage, error) { func Open(path string) (*Storage, error) {
s := Storage{Token: make(map[string]*Shrt), Url: make(map[string]string)} s := Storage{Token: make(map[string]*Data), Url: make(map[string]string)}
// Open db ore create if not exist! // Open db ore create if not exist!
if db, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644); err == nil { if db, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644); err == nil {
json.Unmarshal(StreamToByte(db), &s) json.Unmarshal(StreamToByte(db), &s)
@ -64,9 +71,9 @@ func (s *Storage) Short(URL string, value *string) error {
if _, ok := s.Token[hash[:hashShortestLen]]; !ok { if _, ok := s.Token[hash[:hashShortestLen]]; !ok {
token := hash[:hashShortestLen] token := hash[:hashShortestLen]
t := time.Now() t := time.Now()
s.Token[token] = &Shrt{ s.Token[token] = &Data{
URL: URL, URL: URL,
Date: t.String(), Created: t.String(),
} }
s.Url[hash] = token s.Url[hash] = token
*value = s.Url[hash] *value = s.Url[hash]
@ -78,41 +85,6 @@ func (s *Storage) Short(URL string, value *string) error {
return ErrCreateToken return ErrCreateToken
} }
func (s *Storage) Add(URL string, value *string) (string, error) {
// Check if it is a valide Url found on:
// http://stackoverflow.com/questions/31480710/validate-url-with-standard-package-in-go
_, err := url.ParseRequestURI(URL)
if err != nil {
return "", ErrNoUrl
}
// Create a sha256 Hash from the URL
hash := fmt.Sprintf("%x", sha256.Sum256([]byte(URL)))
// Test if the URL alraedy exist and return the key
if val, ok := s.Url[hash]; ok {
return val, nil
}
// Iterate to the length of hash to get the shortest output
for hashShortestLen := 1; hashShortestLen <= 32; hashShortestLen++ {
// Test if the Token not exist and return the new generated token
if _, ok := s.Token[hash[:hashShortestLen]]; !ok {
token := hash[:hashShortestLen]
t := time.Now()
s.Token[token] = &Shrt{
URL: URL,
Date: t.String(),
}
s.Url[hash] = token
*value = s.Url[hash]
s.Save()
return token, nil
}
}
return "", ErrCreateToken
}
func (s *Storage) Remove(URL string) error { func (s *Storage) Remove(URL string) error {
return nil return nil
} }
@ -129,7 +101,7 @@ func (s *Storage) Get(token string) (string, bool) {
} }
// Get returns the URL for the given token // Get returns the URL for the given token
func (s *Storage) GetAPI(token string, value *Shrt) bool { func (s *Storage) GetAPI(token string, value *Data) bool {
if shrt, ok := s.Token[token]; ok { if shrt, ok := s.Token[token]; ok {
*value = *shrt *value = *shrt
return true return true
Loading…
Cancel
Save