Skip to content

Commit

Permalink
Add ttl feature and redirection complete
Browse files Browse the repository at this point in the history
  • Loading branch information
root27 committed Jun 24, 2024
1 parent 2669b26 commit 748adc2
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 18 deletions.
162 changes: 144 additions & 18 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
package main

import (
"context"
"google.golang.org/api/sheets/v4"
"fmt"
"log"
"net/http"
"net/url"
"os"
"strings"
"sync"
"time"
)

type URLMap map[string]*url.URL

type cached struct {
db URLMap
updatedAt time.Time
type server struct {
db *cached
}

func (c *cached) refreshData() error {

type cached struct {
sync.RWMutex
value URLMap
updatedAt time.Time
ttl time.Duration
sheetProvider *sheet
}

func GetUrls(data [][]interface{}) URLMap {
Expand Down Expand Up @@ -53,32 +58,153 @@ func GetUrls(data [][]interface{}) URLMap {

}

log.Println(output)

return output

}

func main() {

srv, err := sheets.NewService(context.TODO())
port := os.Getenv("PORT")

sheetId := os.Getenv("SHEETID")

sheetName := os.Getenv("SHEETNAME")

ttlValue := os.Getenv("TTL")

ttl := time.Second * 5

if ttlValue != "" {

v, err := time.ParseDuration(ttlValue)

if err != nil {

log.Fatalf("Error parsing time duration %v", err)
}

ttl = v

}

if port == "" {

port = "8080"
}

srv := &server{
db: &cached{
ttl: ttl,
sheetProvider: &sheet{
Name: sheetName,
Id: sheetId,
},
},
}

http.HandleFunc("/", srv.Handler)

log.Printf("Server listening on port: %s", port)

http.ListenAndServe(":"+port, nil)

}

func (s *server) Handler(w http.ResponseWriter, r *http.Request) {

if r.URL.Path == "/" {

s.home(w)

return

}

s.redirector(w, r)

}

func (s *server) home(w http.ResponseWriter) {

w.WriteHeader(http.StatusNotFound)

fmt.Fprintf(w, `<!DOCTYPE html>
<html><head><title>Not found</title></head><body><h1>Not found :(</h1>
<p>This is home page for a URL redirector service.</p>
<p>The URL is missing the shortcut in the path.</p>
</body></html>`)

}

func (c *cached) refreshData() error {

c.Lock()

defer c.Unlock()

if time.Since(c.updatedAt) <= c.ttl {

return nil
}

rows, err := c.sheetProvider.Query()

if err != nil {
log.Fatalf("Unable to retrieve Sheets client: %v", err)

return err

}

//NOTE: Set env variable
spreadsheetId := "1HUIYUXbSX21ZgR93l0ryA-Q_PMoNGeIUlAIlHzT9dNc"
readRange := "A:B"
resp, err := srv.Spreadsheets.Values.Get(spreadsheetId, readRange).Do()
c.value = GetUrls(rows)

c.updatedAt = time.Now()

return nil
}

func (c *cached) Get(query string) (*url.URL, error) {

if err := c.refreshData(); err != nil {

return nil, err
}

c.RLock()

defer c.RUnlock()

return c.value[query], nil

}

func (s *server) redirector(w http.ResponseWriter, r *http.Request) {

if r.Body != nil {
defer r.Body.Close()
}

path := strings.TrimPrefix(r.URL.Path, "/")

url, err := s.db.Get(path)

if err != nil {
log.Fatalf("Unable to retrieve data from sheet: %v", err)

http.Error(w, "Error querying data", http.StatusInternalServerError)

return
}

if len(resp.Values) == 0 {
if url == nil {

w.WriteHeader(http.StatusNotFound)

log.Println("No data available")
fmt.Fprintf(w, `404 Not Found`)

return
}

fmt.Printf("Redirecting %s to %s\n", r.URL, url)

http.Redirect(w, r, url.String(), http.StatusFound)

}
51 changes: 51 additions & 0 deletions sheet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

import (
"context"
"errors"
"google.golang.org/api/sheets/v4"

"log"
)

type sheet struct {
Name string
Id string
}

func (s *sheet) Query() ([][]interface{}, error) {

srv, err := sheets.NewService(context.TODO())

if err != nil {
log.Fatalf("Unable to retrieve Sheets client: %v", err)
}

if s.Id == "" {

log.Fatal("Google Sheets ID not specified")

return nil, errors.New("Google Sheets ID not specified")

}

//NOTE: Set env variable
spreadsheetId := s.Id
readRange := "A:B"

if s.Name != "" {

readRange = s.Name + "!A:B"
}

resp, err := srv.Spreadsheets.Values.Get(spreadsheetId, readRange).Do()
if err != nil {
log.Fatalf("Unable to retrieve data from sheet: %v", err)

return nil, err
}

log.Printf("Number of quering rows: %d\n", len(resp.Values))

return resp.Values, nil
}

0 comments on commit 748adc2

Please sign in to comment.