Skip to content

Commit ecb5d4b

Browse files
committed
feat: add 404 HTML/JSON page
1 parent 7109979 commit ecb5d4b

File tree

3 files changed

+112
-43
lines changed

3 files changed

+112
-43
lines changed

.changelog/19.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
```release-note:feature
2+
`server/short` - Now return 404 HTML page when not found if webbrowser is used or return 404 JSON when CURL is used.
3+
```
4+

short/404.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package short
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/labstack/echo/v4"
7+
)
8+
9+
var tmpl404 = `
10+
<!DOCTYPE html>
11+
<html lang="en">
12+
<head>
13+
<meta charset="UTF-8">
14+
<title>404 Not Found</title>
15+
<style>
16+
html,
17+
body {
18+
height: 100%;
19+
margin: 0;
20+
padding: 0;
21+
}
22+
23+
.container {
24+
height: 100%;
25+
display: flex;
26+
flex-direction: column;
27+
align-items: center;
28+
justify-content: center;
29+
}
30+
31+
.title {
32+
font-size: 3rem;
33+
font-family: sans-serif;
34+
color: #1a202c;
35+
}
36+
37+
.subtitle {
38+
font-size: 1.5rem;
39+
font-family: sans-serif;
40+
color: #718096;
41+
}
42+
43+
.redirect {
44+
font-size: 1rem;
45+
font-family: sans-serif;
46+
color: #718096;
47+
}
48+
</style>
49+
</head>
50+
<body>
51+
<div class="container">
52+
<div class="title">Link Not Found</div>
53+
<div class="subtitle">Sorry, the link you are looking for does not exist.</div>
54+
<div class="redirect">Redirecting you to the home page in 5 seconds... or <a href="/u/">click here</a>.</div>
55+
<script>
56+
setTimeout(function() {
57+
window.location.href = "/u/";
58+
}, 5000);
59+
</script>
60+
</div>
61+
</body>
62+
</html>
63+
`
64+
65+
func (s *Short) handleHTML404(c echo.Context) error {
66+
return c.HTMLBlob(http.StatusNotFound, []byte(tmpl404))
67+
}

short/short.go

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package short
22

33
import (
44
"errors"
5-
"fmt"
65
"net/http"
76
"strings"
87

@@ -28,66 +27,65 @@ func NewHandlers(db sb.Client, e *echo.Echo) *Short {
2827

2928
func (s *Short) redirectLink(c echo.Context) error {
3029
path := c.Param("path")
31-
// path := fmt.Sprintf("/%s", c.Param("path"))
3230

3331
// Split path by "/"
3432
paths := strings.Split(path, "/")
3533

36-
var (
37-
links []models.Link
38-
link models.Link
39-
ns models.Namespace
40-
err error
34+
const (
35+
uaTypeBrowser = iota
36+
uaTypeCLI
4137
)
4238

43-
switch {
44-
case len(paths) == 1:
45-
link, err = s.DB.GetLinkByPath(c.Request().Context(), "/"+path, "default")
46-
if err != nil {
47-
if errors.Is(err, models.ErrNotFound) {
48-
// Link is not found, find if namespace exists and redirect to namespace page
49-
ns, err = s.DB.GetNamespace(c.Request().Context(), paths[0])
50-
if err != nil {
51-
goto RETURNERROR
52-
}
53-
54-
return c.HTML(http.StatusTemporaryRedirect, "/u/"+ns.Name)
39+
var (
40+
ns = "default"
41+
target = paths[0]
42+
43+
uaType = func() int {
44+
ua := c.Request().UserAgent()
45+
46+
// Check if UserAgent is a web browser
47+
switch {
48+
case strings.Contains(ua, "curl") || strings.Contains(ua, "wget"):
49+
return uaTypeCLI
50+
default:
51+
return uaTypeBrowser
5552
}
56-
goto RETURNERROR
57-
}
58-
goto REDIRECT
59-
default:
60-
// find link by path and namespace
61-
ns, err = s.DB.GetNamespace(c.Request().Context(), paths[0])
62-
if err != nil {
63-
goto RETURNERROR
64-
}
65-
66-
links, err = s.DB.ListLinks(c.Request().Context(), ns.Name)
67-
if err != nil {
68-
goto RETURNERROR
69-
}
53+
}()
54+
)
7055

71-
for _, l := range links {
72-
if l.SourcePath == fmt.Sprintf("/%s", paths[1]) && l.NameSpace == ns.Name {
73-
link = l
74-
goto REDIRECT
75-
}
76-
}
56+
if len(paths) != 1 {
57+
ns = paths[0]
58+
target = paths[1]
59+
}
7760

78-
err = models.ErrNotFound
61+
link, err := s.DB.GetLinkByPath(c.Request().Context(), "/"+target, ns)
62+
if err != nil {
7963
goto RETURNERROR
8064
}
8165

66+
goto REDIRECT
67+
8268
RETURNERROR:
69+
8370
switch {
84-
// TODO add html PAGE if UserAgent is a web browser and json output for other UserAgents
8571
case errors.Is(err, models.ErrNotFound):
86-
return c.HTML(http.StatusNotFound, "Not Found")
72+
switch uaType {
73+
case uaTypeBrowser:
74+
return s.handleHTML404(c)
75+
case uaTypeCLI:
76+
return c.JSON(http.StatusNotFound, map[string]string{
77+
"error": "not found",
78+
})
79+
}
8780
case err != nil:
8881
return c.JSON(http.StatusInternalServerError, err)
8982
}
9083

9184
REDIRECT:
92-
return c.Redirect(http.StatusTemporaryRedirect, link.TargetURL)
85+
switch uaType {
86+
case uaTypeCLI:
87+
return c.String(http.StatusTemporaryRedirect, link.TargetURL)
88+
default:
89+
return c.Redirect(http.StatusTemporaryRedirect, link.TargetURL)
90+
}
9391
}

0 commit comments

Comments
 (0)