Skip to content

Commit

Permalink
Add .env & move webserver to a new package
Browse files Browse the repository at this point in the history
  • Loading branch information
steveyiyo committed Apr 4, 2022
1 parent f19490b commit c7525c8
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 187 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Redis_Addr=""
Redis_Pwd=""
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ require (
github.com/go-playground/validator/v10 v10.10.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/joho/godotenv v1.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/magiconair/properties v1.8.5 // indirect
Expand All @@ -42,4 +43,4 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
)

replace "internal" => "../../internal"
replace internal => ../../internal
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
Expand Down
8 changes: 4 additions & 4 deletions internal/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import (
"time"

"github.com/go-redis/redis/v8"
"github.com/steveyiyo/url-shortener/internal/tools"
"github.com/steveyiyo/url-shortener/package/tools"
)

var Redis *redis.Client

// Init Client
func InitRedis() {
func InitRedis(Redis_Addr, Redis_Pwd string) {
Redis = redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
Password: "",
Addr: Redis_Addr,
Password: Redis_Pwd,
DB: 0,
})
}
Expand Down
50 changes: 2 additions & 48 deletions internal/cache/cache_test.go
Original file line number Diff line number Diff line change
@@ -1,60 +1,14 @@
package cache_test
package cache

import (
"context"
"fmt"
"log"
"testing"
"time"

"github.com/go-redis/redis/v8"
"github.com/steveyiyo/url-shortener/internal/tools"
)

var Redis *redis.Client

// Init Client
func InitRedis() {
Redis = redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
Password: "",
DB: 0,
})
}

// Add data to Redis
func AddData(key string, value string, second time.Duration) bool {
ctx := context.Background()

err := Redis.Set(ctx, key, value, second*time.Second).Err()
return tools.ErrCheck(err)
}

// Query data from Redis
func QueryData(key string) (bool, string) {
ctx := context.Background()

var return_status bool
var return_value string

value, err := Redis.Get(ctx, key).Result()
tools.ErrCheck(err)

if err == redis.Nil {
return_status = false
return_value = ""
} else if !tools.ErrCheck(err) {
log.Println(err)
} else {
return_status = true
return_value = value
}
return return_status, return_value
}

// It's a test function.
func TestMain(t *testing.T) {
InitRedis()
InitRedis("127.0.0.1", "")
AddData("hi", "pong", 5)
status, data := QueryData("hi")
if status {
Expand Down
5 changes: 3 additions & 2 deletions internal/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ import (
"time"

_ "github.com/mattn/go-sqlite3"
"github.com/steveyiyo/url-shortener/internal/tools"
"github.com/steveyiyo/url-shortener/package/tools"
)

var db *sql.DB

func Init() {
db, _ = sql.Open("sqlite3", "./data.db")
createTable()
}

// Create file and table
func CreateTable() {
func createTable() {
// Create Table
stmt, err := db.Prepare("CREATE TABLE IF NOT EXISTS urlinfo (ShortID string, Link string, Expireat string);")
tools.ErrCheck(err)
Expand Down
138 changes: 138 additions & 0 deletions internal/webserver/webserver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package webserver

import (
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/steveyiyo/url-shortener/internal/cache"
"github.com/steveyiyo/url-shortener/internal/database"
"github.com/steveyiyo/url-shortener/package/tools"
)

// Predefined variable and struct
type Config struct {
Host string `yaml:"Host"`
Port string `yaml:"Port"`
}

type Result struct {
Status bool
Message string
}

var Listen string
var Host string
var Port string
var URL string

type Data struct {
URL string `json:"url"`
ExpiredAt string `json:"expireAt"`
}

type URLid struct {
ID string `json:"id"`
ShortURL string `json:"shortUrl"`
}

func Init(Init_Listen, Init_Host, Init_Port, Init_URL string) {

Listen = Init_Listen
Host = Init_Host
Port = Init_Port
URL = Init_URL

// Init Web Server
gin.SetMode(gin.ReleaseMode)
route := gin.New()
route.Use(gin.Logger(), gin.Recovery())

config := cors.DefaultConfig()
config.AllowOrigins = []string{"*"}

route.Use(cors.New(config))
route.GET("/:ShortID", RedirectURL)
route.POST("/api/v1/urls", AddURL)

route.Run(Listen + ":" + Port)
}

// AddURL
func AddURL(c *gin.Context) {
// Get JSON Data
var data Data
c.BindJSON(&data)

// Init return result
var return_data URLid

// Check Time and Convert to Unix format
isTimestampOk, timestamp := tools.ConvertTimetoUnix(data.ExpiredAt)

// Check Limit IP
limit_check, _ := cache.QueryData(c.ClientIP())

// Check Limit
if !limit_check {
// Check Link and Time Valid
if tools.CheckLinkValid(data.URL) && (isTimestampOk) {

// Random Short ID
ShortID := tools.RandomString(5)

// Add data to DB
database.AddData(ShortID, data.URL, timestamp)

// Add Limit IP to Redis
cache.AddData(c.ClientIP(), "", 5)

// Return result
return_data = URLid{ID: ShortID, ShortURL: URL + ShortID}
c.JSON(200, return_data)
} else {
// Return result
return_data = URLid{ID: "", ShortURL: ""}
c.JSON(400, return_data)
}
} else {
// Return result
var return_result Result
return_result = Result{Status: false, Message: "Too many requests, please try again later."}
c.JSON(400, return_result)
}
}

// RedirectURL
func RedirectURL(c *gin.Context) {

// Get Short ID from URL
ID := c.Param("ShortID")

// Query Link in Redis
isExist, URL := cache.QueryData(ID)
if !isExist {
// Query Link in DB
Check, Link := database.QueryData(ID)
if Check {
// Add hit to Redis
cache.AddData(ID, Link, 30)
c.Redirect(301, Link)
} else {
// Add miss link to Redis (Not Found or Expire)
cache.AddData(ID, "MISS", 30)

// Return result
var return_result Result
return_result = Result{Status: false, Message: "Not Found."}
c.JSON(404, return_result)
}
} else {
if URL == "MISS" {
// Return result
var return_result Result
return_result = Result{Status: false, Message: "Not Found."}
c.JSON(404, return_result)
} else {
c.Redirect(301, URL)
}
}
}
Loading

0 comments on commit c7525c8

Please sign in to comment.