Skip to content

Commit 28709d5

Browse files
committed
style index.html
1 parent 1ad88b3 commit 28709d5

File tree

10 files changed

+310
-2
lines changed

10 files changed

+310
-2
lines changed

.idea/dataSources.xml

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

db.sqlite

12 KB
Binary file not shown.

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
module github.com/nyae44/url-shortener-app
22

33
go 1.23
4+
5+
require github.com/mattn/go-sqlite3 v1.14.24

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
2+
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=

internal/controllers/index.go

+17
Original file line numberDiff line numberDiff line change
@@ -1 +1,18 @@
11
package controllers
2+
3+
import (
4+
"html/template"
5+
"net/http"
6+
)
7+
8+
func ShowIndex(w http.ResponseWriter, r *http.Request) {
9+
temp, err := template.ParseFiles("internal/views/index.html")
10+
if err != nil {
11+
http.Error(w, err.Error(), http.StatusInternalServerError)
12+
return
13+
}
14+
if err = temp.Execute(w, nil); err != nil {
15+
http.Error(w, err.Error(), http.StatusInternalServerError)
16+
return
17+
}
18+
}

internal/controllers/shorten.go

+62
Original file line numberDiff line numberDiff line change
@@ -1 +1,63 @@
11
package controllers
2+
3+
import (
4+
"database/sql"
5+
"github.com/nyae44/url-shortener-app/internal/db"
6+
"github.com/nyae44/url-shortener-app/internal/url"
7+
"html/template"
8+
"net/http"
9+
"strings"
10+
)
11+
12+
func Shorten(lite *sql.DB) http.HandlerFunc {
13+
return func(w http.ResponseWriter, r *http.Request) {
14+
if r.Method != http.MethodPost {
15+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
16+
return
17+
}
18+
originalURL := r.FormValue("url")
19+
if originalURL == "" {
20+
http.Error(w, "url is no provided", http.StatusBadRequest)
21+
return
22+
}
23+
24+
if !strings.HasPrefix(originalURL, "http://") && !strings.HasPrefix(originalURL, "https://") {
25+
originalURL = "https://" + originalURL
26+
}
27+
28+
// shorten url
29+
shortURL := url.Shorten(originalURL)
30+
31+
if err := db.StoreURL(lite, shortURL, originalURL); err != nil {
32+
http.Error(w, err.Error(), http.StatusInternalServerError)
33+
return
34+
}
35+
36+
data := map[string]string{
37+
"ShortURL": shortURL,
38+
}
39+
t, err := template.ParseFiles("internal/views/shorten.html")
40+
if err != nil {
41+
http.Error(w, err.Error(), http.StatusInternalServerError)
42+
return
43+
}
44+
if err := t.Execute(w, data); err != nil {
45+
http.Error(w, err.Error(), http.StatusInternalServerError)
46+
}
47+
}
48+
}
49+
func Proxy(lite *sql.DB) http.HandlerFunc {
50+
return func(w http.ResponseWriter, r *http.Request) {
51+
shortURL := r.URL.Path[1:]
52+
if shortURL == "" {
53+
http.Error(w, "URL not provided", http.StatusBadRequest)
54+
return
55+
}
56+
origUrl, err := db.GetOriginalURL(lite, shortURL)
57+
if err != nil {
58+
http.Error(w, err.Error(), http.StatusNotFound)
59+
return
60+
}
61+
http.Redirect(w, r, origUrl, http.StatusPermanentRedirect)
62+
}
63+
}

internal/db/db.go

+32
Original file line numberDiff line numberDiff line change
@@ -1 +1,33 @@
11
package db
2+
3+
import "database/sql"
4+
5+
// createTable ensures the URLs table exists
6+
func CreateTable(db *sql.DB) error {
7+
query := `
8+
CREATE TABLE IF NOT EXISTS urls (
9+
id INTEGER PRIMARY KEY AUTOINCREMENT,
10+
short_url TEXT NOT NULL,
11+
original_url TEXT NOT NULL
12+
);`
13+
_, err := db.Exec(query)
14+
return err
15+
}
16+
17+
// storeURL inserts a new short URL and the original URL into the database
18+
func StoreURL(db *sql.DB, shortURL string, originalURL string) error {
19+
query := `INSERT INTO urls(short_url, original_url) VALUES(?, ?)`
20+
_, err := db.Exec(query, shortURL, originalURL)
21+
return err
22+
}
23+
24+
// getOriginalURL fetches the original URL by the short URL
25+
func GetOriginalURL(db *sql.DB, shortURL string) (string, error) {
26+
var originalURL string
27+
query := `SELECT original_url FROM urls WHERE short_url = ?`
28+
err := db.QueryRow(query, shortURL).Scan(&originalURL)
29+
if err != nil {
30+
return "", err
31+
}
32+
return originalURL, nil
33+
}

internal/url/url.go

+15
Original file line numberDiff line numberDiff line change
@@ -1 +1,16 @@
11
package url
2+
3+
import (
4+
"crypto/sha256"
5+
"encoding/hex"
6+
"fmt"
7+
)
8+
9+
func Shorten(originalURL string) string {
10+
h := sha256.New()
11+
h.Write([]byte(originalURL))
12+
fmt.Println(h.Sum(nil))
13+
hash := hex.EncodeToString(h.Sum(nil))
14+
shortURL := hash[:8]
15+
return shortURL
16+
}

internal/views/index.html

+131-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,135 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
13
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>URL Shortener</title>
7+
<style>
8+
/* Reset some default styles */
9+
body, h1, form, input, button, div {
10+
margin: 0;
11+
padding: 0;
12+
box-sizing: border-box;
13+
}
214

15+
body {
16+
font-family: 'Arial', sans-serif;
17+
background-color: #f4f4f9;
18+
color: #333;
19+
display: flex;
20+
justify-content: center;
21+
align-items: center;
22+
min-height: 100vh;
23+
}
24+
25+
h1 {
26+
text-align: center;
27+
color: #4CAF50;
28+
font-size: 2rem;
29+
margin-bottom: 20px;
30+
}
31+
32+
form {
33+
background: #fff;
34+
padding: 20px;
35+
border-radius: 8px;
36+
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
37+
display: flex;
38+
flex-direction: column;
39+
align-items: center;
40+
width: 100%;
41+
max-width: 400px;
42+
}
43+
44+
input[type="text"] {
45+
padding: 12px 15px;
46+
width: 100%;
47+
border: 1px solid #ddd;
48+
border-radius: 6px;
49+
margin-bottom: 10px;
50+
font-size: 1rem;
51+
}
52+
53+
button {
54+
padding: 12px 20px;
55+
background-color: #4CAF50;
56+
color: #fff;
57+
border: none;
58+
border-radius: 6px;
59+
cursor: pointer;
60+
font-size: 1rem;
61+
width: 100%;
62+
transition: background-color 0.3s ease;
63+
}
64+
65+
button:hover {
66+
background-color: #45a049;
67+
}
68+
69+
#result {
70+
margin-top: 20px;
71+
text-align: center;
72+
}
73+
74+
#result a {
75+
color: #4CAF50;
76+
text-decoration: none;
77+
font-weight: bold;
78+
}
79+
80+
#result a:hover {
81+
text-decoration: underline;
82+
}
83+
84+
/* Responsive styling */
85+
@media (max-width: 480px) {
86+
h1 {
87+
font-size: 1.5rem;
88+
}
89+
90+
input[type="text"], button {
91+
font-size: 0.9rem;
92+
}
93+
}
94+
</style>
95+
<script>
96+
document.addEventListener('DOMContentLoaded', function () {
97+
const form = document.getElementById('shortenForm');
98+
const resultDiv = document.getElementById('result');
99+
100+
if (form) {
101+
form.addEventListener('submit', async function (event) {
102+
event.preventDefault();
103+
const url = document.getElementById('urlInput').value;
104+
105+
const response = await fetch('/shorten', {
106+
method: 'POST',
107+
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
108+
body: `url=${encodeURIComponent(url)}`
109+
});
110+
111+
// Insert HTML response and execute script
112+
const htmlContent = await response.text();
113+
resultDiv.innerHTML = htmlContent;
114+
115+
// Manually execute script
116+
const script = resultDiv.querySelector('script');
117+
if (script) {
118+
eval(script.innerText)
119+
}
120+
});
121+
}
122+
});
123+
</script>
3124
</head>
4125
<body>
5-
<h1>URL Shortener</h1>
6-
</body>
126+
<main>
127+
<h1>URL Shortener</h1>
128+
<form id="shortenForm" action="/shorten" method="POST">
129+
<input type="text" name="url" id="urlInput" placeholder="Enter URL to shorten" required>
130+
<button type="submit">Shorten</button>
131+
</form>
132+
<div id="result"></div>
133+
</main>
134+
</body>
135+
</html>

internal/views/shorten.html

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>URL Shortened</title>
7+
</head>
8+
<body>
9+
<h1>Shortened URL</h1>
10+
<p>Your shortened URL is: <a href="/{{.ShortURL}}" target="_blank">{{.ShortURL}}</a></p>
11+
<button id="copyButton" data-url="{{.ShortURL}}">Copy to clipboard</button>
12+
13+
<script>
14+
15+
const copyButton = document.getElementById('copyButton');
16+
const url = copyButton.getAttribute('data-url');
17+
18+
// Log the URL to ensure data-url is populated
19+
console.log("Data URL to copy:", url);
20+
21+
copyButton.addEventListener('click', function () {
22+
if (navigator.clipboard && navigator.clipboard.writeText) {
23+
navigator.clipboard.writeText("http://localhost:8080" + url).then(() => {
24+
alert('URL copied to clipboard!');
25+
}).catch(err => {
26+
console.error('Failed to copy:', err);
27+
});
28+
} else {
29+
alert('Clipboard API not supported in this browser.');
30+
}
31+
});
32+
</script>
33+
</body>
34+
</html>

0 commit comments

Comments
 (0)