Skip to content
This repository was archived by the owner on Nov 19, 2024. It is now read-only.

Commit 023aa28

Browse files
Release v1.1.0: Web interface for Simple URL shortener
Changes: 1. Added static folder, which contains new web interface to add new URLs (big thanks to @vadimvalov and @SaraUlan) 2. Changed API and now DELETE and POST Requests are using root URL (before it was /url) 3. Added Handle to use static files in main.go 4. Added .gitignore for website 5. Added greeting.go file, which redirect users from / request to the website
1 parent ff29516 commit 023aa28

File tree

14 files changed

+126
-8
lines changed

14 files changed

+126
-8
lines changed

cmd/simple-url-shortener/main.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http"
88
"os"
99
"simple-url-shortener/internal/config"
10+
"simple-url-shortener/internal/http-server/handlers/greeting"
1011
"simple-url-shortener/internal/http-server/handlers/redirect"
1112
"simple-url-shortener/internal/http-server/handlers/url/delete"
1213
"simple-url-shortener/internal/http-server/handlers/url/save"
@@ -56,14 +57,21 @@ func main() {
5657
router.Use(middleware.Recoverer)
5758
router.Use(middleware.URLFormat)
5859

60+
fs := http.FileServer(http.Dir("./static"))
61+
router.Handle("/static/*", http.StripPrefix("/static/", fs))
62+
5963
router.Route("/url", func(r chi.Router) {
6064
r.Use(middleware.BasicAuth("simple-url-shortener", map[string]string{
6165
cfg.HTTPServer.User: cfg.HTTPServer.Password,
6266
}))
6367

64-
r.Post("/", save.New(log, storage))
65-
r.Delete("/{alias}", delete.New(log, storage))
68+
//r.Post("/", save.New(log, storage))
69+
//r.Delete("/{alias}", delete.New(log, storage))
6670
})
71+
72+
router.Get("/", greeting.New(log, "./static"))
73+
router.Post("/", save.New(log, storage))
74+
router.Delete("/{alias}", delete.New(log, storage))
6775
router.Get("/{alias}", redirect.New(log, storage))
6876

6977
log.Info("starting server", slog.String("address", cfg.Address))
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package greeting
2+
3+
import (
4+
"fmt"
5+
"github.com/go-chi/chi/v5/middleware"
6+
"github.com/go-chi/render"
7+
"golang.org/x/exp/slog"
8+
"net/http"
9+
"os"
10+
"simple-url-shortener/internal/lib/logger/sl"
11+
)
12+
13+
func New(log *slog.Logger, staticDir string) http.HandlerFunc {
14+
15+
return func(w http.ResponseWriter, r *http.Request) {
16+
const op = "handlers.greeting.New"
17+
18+
log = log.With(
19+
slog.String("op", op),
20+
slog.String("request_id", middleware.GetReqID(r.Context())),
21+
)
22+
23+
indexFilePath := fmt.Sprintf("%s/index.html", staticDir)
24+
indexHTML, err := readHtmlFromFile(indexFilePath)
25+
if err != nil {
26+
log.Error("Failed to read index.html", sl.Err(err))
27+
render.Status(r, http.StatusInternalServerError)
28+
render.PlainText(w, r, "Internal Server Error")
29+
return
30+
}
31+
32+
w.Header().Set("Content-Type", "text/html; charset=utf-8")
33+
w.WriteHeader(http.StatusOK)
34+
_, _ = w.Write([]byte(indexHTML))
35+
}
36+
}
37+
38+
func readHtmlFromFile(filePath string) (string, error) {
39+
bs, err := os.ReadFile(filePath)
40+
41+
if err != nil {
42+
return "", err
43+
}
44+
45+
return string(bs), nil
46+
}
File renamed without changes.
File renamed without changes.

web/index.html renamed to static/index.html

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
<title>SUS</title>
88
<meta name="description" content="" />
99
<meta name="keywords" content="" />
10-
<link rel="stylesheet" href="index.css" />
10+
<link rel="stylesheet" href="/static/index.css" />
11+
<script src="/static/main.js"></script>
1112

1213
<link
1314
rel="stylesheet"
@@ -33,7 +34,7 @@
3334

3435
<body
3536
class="leading-normal tracking-normal text-indigo-400 m-6 bg-cover bg-fixed"
36-
style="background-image: url('./image/header.jpg')"
37+
style="background-image: url('/static/image/header.jpg')"
3738
>
3839
<div class="h-full">
3940
<!--Nav-->
@@ -110,13 +111,13 @@
110111
<div class="mb-4">
111112
<label
112113
class="block text-blue-300 py-2 font-bold mb-2"
113-
for="emailaddress"
114+
for="urlInput"
114115
>
115116
Enter your URL:
116117
</label>
117118
<input
118119
class="shadow appearance-none border rounded w-full p-3 text-gray-700 leading-tight focus:ring transform transition hover:scale-105 duration-300 ease-in-out"
119-
id="emailaddress"
120+
id="urlInput"
120121
type="text"
121122
placeholder="https://example.com/anything_here"
122123
/>
@@ -162,13 +163,13 @@
162163
<div class="mb-4">
163164
<label
164165
class="block text-blue-300 py-2 font-bold mb-2"
165-
for="emailaddress"
166+
for="urlOutput"
166167
>
167168
Here is your link:
168169
</label>
169170
<input
170171
class="shadow appearance-none border rounded w-full p-3 text-gray-700 leading-tight focus:ring transform transition hover:scale-105 duration-300 ease-in-out"
171-
id="emailaddress"
172+
id="urlOutput"
172173
type="text"
173174
placeholder="https://example.com/anything_here"
174175
/>

static/main.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
document.addEventListener('DOMContentLoaded', function () {
2+
const cutButton = document.getElementById('cut');
3+
const urlInput = document.getElementById('urlInput');
4+
const urlOutput = document.getElementById('urlOutput');
5+
const copyButton = document.getElementById('openModalButton');
6+
7+
cutButton.addEventListener('click', function () {
8+
const url = urlInput.value.trim();
9+
if (url) {
10+
// Create a JSON object with the URL data
11+
const requestData = {url};
12+
13+
// Make the AJAX POST request
14+
fetch('/', {
15+
method: 'POST',
16+
headers: {
17+
'Content-Type': 'application/json',
18+
},
19+
body: JSON.stringify(requestData),
20+
})
21+
.then((response) => response.json())
22+
.then((data) => {
23+
if (data.status === 'OK' && data.alias) {
24+
// Update the output input text with the shortened URL
25+
urlOutput.value = `https://sus.kz/${data.alias}`;
26+
} else {
27+
alert('Failed to shorten the URL. Error: ' + data.error);
28+
}
29+
})
30+
.catch((error) => {
31+
console.error('Error:', error);
32+
alert('An error occurred while shortening the URL. Please try again.');
33+
});
34+
} else {
35+
alert('Please enter a valid URL.');
36+
}
37+
});
38+
39+
// Add event listener to copy button
40+
copyButton.addEventListener('click', function () {
41+
// Check if the Clipboard API is available
42+
if (navigator.clipboard) {
43+
// Use the Clipboard API to copy the text
44+
navigator.clipboard.writeText(urlOutput.value)
45+
.then(function () {
46+
// Notify the user that the text has been copied
47+
// alert('Copied to clipboard: ' + urlOutput.value);
48+
})
49+
.catch(function (err) {
50+
console.error('Failed to copy text: ', err);
51+
});
52+
} else {
53+
// Clipboard API is not available, fall back to the old method
54+
const textArea = document.createElement('textarea');
55+
textArea.value = urlOutput.value;
56+
document.body.appendChild(textArea);
57+
textArea.select();
58+
document.execCommand('copy');
59+
document.body.removeChild(textArea);
60+
// alert('Copied to clipboard: ' + urlOutput.value);
61+
}
62+
});
63+
});
File renamed without changes.

0 commit comments

Comments
 (0)