-
Notifications
You must be signed in to change notification settings - Fork 0
/
handlers.go
157 lines (134 loc) · 3.27 KB
/
handlers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package src
import (
"database/sql"
"encoding/json"
"log"
"net/http"
"strings"
_ "github.com/mattn/go-sqlite3"
)
var Db *sql.DB
func init() {
var err error
Db, err = sql.Open("sqlite3", "urls.db")
if err != nil {
panic(err)
}
// Create table and indexes if they don't exist
stmt, err := Db.Prepare(
`CREATE TABLE IF NOT EXISTS url_mappings (
id TEXT,
hash TEXT,
url TEXT,
PRIMARY KEY (id),
UNIQUE (hash)
);
CREATE INDEX IF NOT EXISTS idx_url_mappings_hash ON url_mappings (hash);`)
if err != nil {
panic(err)
}
stmt.Exec()
}
func ShortenUrl(w http.ResponseWriter, r *http.Request) {
var req Request
err := json.NewDecoder(r.Body).Decode(&req)
// Check if method is POST and return error if not
if r.Method != http.MethodPost {
JsonError(
w,
map[string]string{"error": "Method not allowed"},
http.StatusMethodNotAllowed,
)
return
}
// Check if decoding was successful
if err != nil {
log.Println(err)
JsonError(
w,
map[string]string{"error": "Decoding error"},
http.StatusBadRequest,
)
return
}
// Validate url
if !ValidateUrl(req.Url) {
log.Println("Invalid URL")
JsonError(
w,
map[string]string{"error": "Invalid URL"},
http.StatusBadRequest,
)
return
}
// Strip trailing slash
req.Url = TrimSlash(req.Url)
// Generate hash
hash := GenerateHash(req.Url)
// Check if hash exists and return id if it does
row := Db.QueryRow("SELECT id FROM url_mappings WHERE hash=?", hash)
var id string
err = row.Scan(&id)
if err != nil && err != sql.ErrNoRows {
log.Println(err)
JsonError(
w,
map[string]string{"error": "Error querying database"},
http.StatusInternalServerError,
)
return
}
// If id exists, return it as tinyurl and exit
if id != "" {
res := Response{TinyUrl: GetQualifiedTinyUrl(r, id)}
jsonRes, _ := json.Marshal(res)
w.Header().Set("Content-Type", "application/json")
w.Write(jsonRes)
return
}
// If id doesn't exist, generate a new one
id = GenerateId(7)
// Insert new mapping
_, err = Db.Exec("INSERT INTO url_mappings VALUES (?, ?, ?)", id, hash, req.Url)
if err != nil {
log.Println(err)
JsonError(
w,
map[string]string{"error": "Error inserting into database"},
http.StatusInternalServerError,
)
return
}
// Return new ID
res := Response{TinyUrl: GetQualifiedTinyUrl(r, id)}
jsonRes, _ := json.Marshal(res)
w.Header().Set("Content-Type", "application/json")
w.Write(jsonRes)
}
func RedirectUrl(w http.ResponseWriter, r *http.Request) {
// Check if method is GET and return error if not
if r.Method != http.MethodGet {
JsonError(
w,
map[string]string{"error": "Method not allowed"},
http.StatusMethodNotAllowed,
)
return
}
// Get the short URL from the request path
id := strings.TrimPrefix(TrimSlash(r.URL.Path), "r/")
// Query the database for the long URL
var url string
err := Db.QueryRow("SELECT url FROM url_mappings WHERE id = ?", id).Scan(&url)
// If the short URL is not found in the database, return a 404 error
if err != nil {
log.Println(err)
JsonError(w, map[string]string{"error": "Not found"}, http.StatusNotFound)
return
}
// Redirect to the long URL
http.Redirect(w, r, url, http.StatusSeeOther)
}
func DisplayIndex(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "src/index.html")
}