Compare commits
8 commits
Author | SHA1 | Date | |
---|---|---|---|
ptrcnull | 8c8f2f55ef | ||
ptrcnull | 745e465034 | ||
ptrcnull | b9a275b0d5 | ||
ptrcnull | 4394883200 | ||
ptrcnull | d724154635 | ||
ptrcnull | 5fb29668c8 | ||
ptrcnull | cda0a8f737 | ||
ptrcnull | 1801c98866 |
7
.forgejo/workflows/build.yaml
Normal file
7
.forgejo/workflows/build.yaml
Normal file
|
@ -0,0 +1,7 @@
|
|||
on: [push]
|
||||
jobs:
|
||||
test:
|
||||
runs-on: docker
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: go build
|
|
@ -6,4 +6,6 @@
|
|||
|
||||
Environmental variables:
|
||||
- `SHORTEN_HOST` - hostname
|
||||
- `SHORTEN_BIND` - bind address (default: `127.0.0.1:4488`)
|
||||
- `SHORTEN_MAIL` - optional email for support/abuse reports
|
||||
- `POSTGRES_URI` - lib/pq connection string (see [here](https://pkg.go.dev/github.com/lib/pq#section-documentation))
|
||||
|
|
11
index.html
11
index.html
|
@ -10,6 +10,7 @@
|
|||
height: calc(100vh - 32px);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
form {
|
||||
display: flex;
|
||||
|
@ -31,10 +32,15 @@
|
|||
font-size: 30px;
|
||||
}
|
||||
a {
|
||||
font-family: sans-serif;
|
||||
color: #dddddd;
|
||||
text-decoration: none;
|
||||
}
|
||||
footer {
|
||||
width: 100vw;
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -45,5 +51,8 @@
|
|||
<input type="text" {{ with .error }}placeholder="{{ . }}"{{ end }} name="url" autocomplete="off" autocapitalize="off">
|
||||
</form>
|
||||
{{ end }}
|
||||
{{ if .mail }}
|
||||
<footer>support, takedown, abuse reports: {{ .mail }}</footer>
|
||||
{{ end }}
|
||||
</body>
|
||||
</html>
|
||||
|
|
24
main.go
24
main.go
|
@ -12,6 +12,7 @@ import (
|
|||
"time"
|
||||
|
||||
"database/sql"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
@ -70,7 +71,11 @@ func main() {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
panic(http.ListenAndServe("127.0.0.1:4488", &Handler{db: db}))
|
||||
bind := os.Getenv("SHORTEN_BIND")
|
||||
if bind == "" {
|
||||
bind = "127.0.0.1:4488"
|
||||
}
|
||||
panic(http.ListenAndServe(bind, &Handler{db: db}))
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
|
@ -91,8 +96,6 @@ func (h *Handler) ServeHTTP(wr http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
func (h *Handler) HomepageHandler(wr http.ResponseWriter, req *http.Request) {
|
||||
log.Println("HomepageHandler")
|
||||
|
||||
url := req.URL.Query().Get("url")
|
||||
if url != "" {
|
||||
code, err := h.GetCode(url, req.RemoteAddr)
|
||||
|
@ -104,12 +107,10 @@ func (h *Handler) HomepageHandler(wr http.ResponseWriter, req *http.Request) {
|
|||
wr.Write([]byte("https://" + os.Getenv("SHORTEN_HOST") + "/" + code))
|
||||
}
|
||||
|
||||
Render(wr, nil)
|
||||
Render(wr, map[string]string{})
|
||||
}
|
||||
|
||||
func (h *Handler) CreateHandler(wr http.ResponseWriter, req *http.Request) {
|
||||
log.Println("CreateHandler")
|
||||
|
||||
ip := req.RemoteAddr
|
||||
if strings.HasPrefix(ip, "127.0.0.1") {
|
||||
proxyIp := strings.Split(req.Header.Get("X-Forwarded-For"), ",")[0]
|
||||
|
@ -119,7 +120,7 @@ func (h *Handler) CreateHandler(wr http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
req.ParseForm()
|
||||
code, err := h.GetCode(req.Form.Get("url"), req.RemoteAddr)
|
||||
code, err := h.GetCode(req.Form.Get("url"), ip)
|
||||
if err != nil {
|
||||
Render(wr, map[string]string{"error": err.Error()})
|
||||
} else {
|
||||
|
@ -128,8 +129,6 @@ func (h *Handler) CreateHandler(wr http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
func (h *Handler) RedirectHandler(wr http.ResponseWriter, req *http.Request) {
|
||||
log.Println("RedirectHandler")
|
||||
|
||||
code := req.URL.Path[1:]
|
||||
var url string
|
||||
var hits uint64
|
||||
|
@ -138,12 +137,12 @@ func (h *Handler) RedirectHandler(wr http.ResponseWriter, req *http.Request) {
|
|||
log.Println("hits query error:", err)
|
||||
}
|
||||
wr.Header().Set("Location", "/")
|
||||
wr.WriteHeader(http.StatusMovedPermanently)
|
||||
wr.WriteHeader(http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
_, _ = h.db.Exec(`UPDATE urls SET hits = $1 WHERE code = $2`, hits + 1, code)
|
||||
_, _ = h.db.Exec(`UPDATE urls SET hits = $1 WHERE code = $2`, hits+1, code)
|
||||
}()
|
||||
|
||||
wr.Header().Set("Location", url)
|
||||
|
@ -164,7 +163,6 @@ func (h *Handler) CodeExists(code string) bool {
|
|||
}
|
||||
|
||||
func (h *Handler) GetCode(url string, ip string) (string, error) {
|
||||
log.Printf("url: %#v\n", url)
|
||||
if !strings.HasPrefix(url, "http") || !govalidator.IsURL(url) {
|
||||
return "", fmt.Errorf("invalid URL")
|
||||
}
|
||||
|
@ -175,6 +173,7 @@ func (h *Handler) GetCode(url string, ip string) (string, error) {
|
|||
return code, nil
|
||||
}
|
||||
if err != sql.ErrNoRows {
|
||||
log.Println("sql error:", err)
|
||||
return "", fmt.Errorf("query: %w", err)
|
||||
}
|
||||
|
||||
|
@ -196,6 +195,7 @@ func (h *Handler) GetCode(url string, ip string) (string, error) {
|
|||
func Render(wr http.ResponseWriter, data map[string]string) {
|
||||
wr.Header().Set("Content-Type", "text/html")
|
||||
data["host"] = os.Getenv("SHORTEN_HOST")
|
||||
data["mail"] = os.Getenv("SHORTEN_MAIL")
|
||||
err := tmpl.Execute(wr, data)
|
||||
if err != nil {
|
||||
log.Println("error writing template:", err)
|
||||
|
|
Loading…
Reference in a new issue