1 package main
2
3 import (
4 "context"
5 "io/ioutil"
6 "net/http"
7 "os"
8
9 "github.com/jackc/pgx/v4"
10 "github.com/jackc/pgx/v4/log/log15adapter"
11 "github.com/jackc/pgx/v4/pgxpool"
12 log "gopkg.in/inconshreveable/log15.v2"
13 )
14
15 var db *pgxpool.Pool
16
17 func getUrlHandler(w http.ResponseWriter, req *http.Request) {
18 var url string
19 err := db.QueryRow(context.Background(), "select url from shortened_urls where id=$1", req.URL.Path).Scan(&url)
20 switch err {
21 case nil:
22 http.Redirect(w, req, url, http.StatusSeeOther)
23 case pgx.ErrNoRows:
24 http.NotFound(w, req)
25 default:
26 http.Error(w, "Internal server error", http.StatusInternalServerError)
27 }
28 }
29
30 func putUrlHandler(w http.ResponseWriter, req *http.Request) {
31 id := req.URL.Path
32 var url string
33 if body, err := ioutil.ReadAll(req.Body); err == nil {
34 url = string(body)
35 } else {
36 http.Error(w, "Internal server error", http.StatusInternalServerError)
37 return
38 }
39
40 if _, err := db.Exec(context.Background(), `insert into shortened_urls(id, url) values ($1, $2)
41 on conflict (id) do update set url=excluded.url`, id, url); err == nil {
42 w.WriteHeader(http.StatusOK)
43 } else {
44 http.Error(w, "Internal server error", http.StatusInternalServerError)
45 }
46 }
47
48 func deleteUrlHandler(w http.ResponseWriter, req *http.Request) {
49 if _, err := db.Exec(context.Background(), "delete from shortened_urls where id=$1", req.URL.Path); err == nil {
50 w.WriteHeader(http.StatusOK)
51 } else {
52 http.Error(w, "Internal server error", http.StatusInternalServerError)
53 }
54 }
55
56 func urlHandler(w http.ResponseWriter, req *http.Request) {
57 switch req.Method {
58 case "GET":
59 getUrlHandler(w, req)
60
61 case "PUT":
62 putUrlHandler(w, req)
63
64 case "DELETE":
65 deleteUrlHandler(w, req)
66
67 default:
68 w.Header().Add("Allow", "GET, PUT, DELETE")
69 w.WriteHeader(http.StatusMethodNotAllowed)
70 }
71 }
72
73 func main() {
74 logger := log15adapter.NewLogger(log.New("module", "pgx"))
75
76 poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL"))
77 if err != nil {
78 log.Crit("Unable to parse DATABASE_URL", "error", err)
79 os.Exit(1)
80 }
81
82 poolConfig.ConnConfig.Logger = logger
83
84 db, err = pgxpool.ConnectConfig(context.Background(), poolConfig)
85 if err != nil {
86 log.Crit("Unable to create connection pool", "error", err)
87 os.Exit(1)
88 }
89
90 http.HandleFunc("/", urlHandler)
91
92 log.Info("Starting URL shortener on localhost:8080")
93 err = http.ListenAndServe("localhost:8080", nil)
94 if err != nil {
95 log.Crit("Unable to start web server", "error", err)
96 os.Exit(1)
97 }
98 }
99
View as plain text