mirror of
https://github.com/4rkal/shortr.git
synced 2024-09-20 06:07:23 +03:00
Compare commits
9 Commits
d1f48ae00e
...
d7f09c07dd
Author | SHA1 | Date | |
---|---|---|---|
|
d7f09c07dd | ||
|
6dbe9aa2ce | ||
21518a691f | |||
|
620306b6ae | ||
b12d275fbf | |||
04dc9ae612 | |||
|
c1fc3dd16e | ||
|
77e78d0373 | ||
|
64dcf4614e |
5
.dockerignore
Normal file
5
.dockerignore
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/tmp
|
||||||
|
*.log
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
README.md
|
25
Dockerfile
Normal file
25
Dockerfile
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
FROM golang:1.23-alpine AS builder
|
||||||
|
|
||||||
|
ENV GO111MODULE=on \
|
||||||
|
CGO_ENABLED=0 \
|
||||||
|
GOOS=linux \
|
||||||
|
GOARCH=amd64
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN go build -o /shortr ./cmd/main.go
|
||||||
|
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
WORKDIR /
|
||||||
|
|
||||||
|
COPY --from=builder /shortr /shortr
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["/shortr"]
|
27
README.md
27
README.md
@ -1,3 +1,28 @@
|
|||||||
# shortr
|
# shortr
|
||||||
|
|
||||||
A blazingly fast url shortener, written in go
|
***Blazingly fast*** url shortener, written in go
|
||||||
|
|
||||||
|
## Instaallation
|
||||||
|
The first step is cloning this repo:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git clone https://github.com/4rkal/shortr && cd shortr
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit the `docker-compose.yml` and set a base url:
|
||||||
|
|
||||||
|
command: ["/shortr", "--url", "YOUR_URL"]
|
||||||
|
|
||||||
|
Change the `YOUR_URL`, to the url where your app served from (eg app.4rkal.com)
|
||||||
|
|
||||||
|
Now you can run the application via docker.
|
||||||
|
|
||||||
|
### Using docker
|
||||||
|
Assuming that you have docker compose installed simply run
|
||||||
|
|
||||||
|
```
|
||||||
|
docker compose up
|
||||||
|
```
|
||||||
|
|
||||||
|
Now the application should be available by vissiting `localhost:8080`
|
||||||
|
|
||||||
|
39
cmd/main.go
39
cmd/main.go
@ -1,10 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/4rkal/shortr/models"
|
"github.com/4rkal/shortr/models"
|
||||||
@ -26,6 +29,14 @@ type StatsFormData struct {
|
|||||||
|
|
||||||
var linkMap = map[string]*models.Link{}
|
var linkMap = map[string]*models.Link{}
|
||||||
|
|
||||||
|
var baseurl *string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
baseurl = flag.String("url", "127.0.0.1:8080", "The url (domain) that the server is running on")
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
|
|
||||||
@ -50,6 +61,10 @@ func RedirectHandler(c echo.Context) error {
|
|||||||
return c.String(http.StatusNotFound, "Link not found")
|
return c.String(http.StatusNotFound, "Link not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(link.Url, "://") {
|
||||||
|
link.Url = "http://" + link.Url
|
||||||
|
}
|
||||||
|
|
||||||
link.Clicks = link.Clicks + 1
|
link.Clicks = link.Clicks + 1
|
||||||
|
|
||||||
return c.Redirect(http.StatusMovedPermanently, link.Url)
|
return c.Redirect(http.StatusMovedPermanently, link.Url)
|
||||||
@ -80,7 +95,7 @@ func SubmitHandler(c echo.Context) error {
|
|||||||
|
|
||||||
linkMap[id] = &models.Link{Id: id, Url: data.Url}
|
linkMap[id] = &models.Link{Id: id, Url: data.Url}
|
||||||
|
|
||||||
return views.Submission(id).Render(c.Request().Context(), c.Response())
|
return views.Submission(id, *baseurl).Render(c.Request().Context(), c.Response())
|
||||||
}
|
}
|
||||||
|
|
||||||
func StatsHandler(c echo.Context) error {
|
func StatsHandler(c echo.Context) error {
|
||||||
@ -102,8 +117,26 @@ func StatsSubmissionHandler(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isURL(s string) bool {
|
func isURL(s string) bool {
|
||||||
_, err := url.ParseRequestURI(s)
|
if !strings.Contains(s, "://") {
|
||||||
return err == nil
|
s = "http://" + s
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedUrl, err := url.ParseRequestURI(s)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsedUrl.Host == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
domainRegex := `^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$`
|
||||||
|
matched, err := regexp.MatchString(domainRegex, parsedUrl.Host)
|
||||||
|
if err != nil || !matched {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateRandomString(length int) string {
|
func generateRandomString(length int) string {
|
||||||
|
10
docker-compose.yml
Normal file
10
docker-compose.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
shortr:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
restart: unless-stopped
|
||||||
|
command: ["/shortr", "--url", "app.4rkal.com"]
|
@ -7,7 +7,7 @@ templ Index(){
|
|||||||
<h1>Blazingly fast URL shortener</h1>
|
<h1>Blazingly fast URL shortener</h1>
|
||||||
<p>Shorten URLs and track clicks</p>
|
<p>Shorten URLs and track clicks</p>
|
||||||
<form action="/submit" method="post">
|
<form action="/submit" method="post">
|
||||||
<input type="url" id="url" name="url" placeholder="Enter URL here...">
|
<input type="text" id="url" name="url" placeholder="Enter URL here...">
|
||||||
<button type="submit">Shorten URL</button>
|
<button type="submit">Shorten URL</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -41,7 +41,7 @@ func Index() templ.Component {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
ctx = templ.InitializeContext(ctx)
|
ctx = templ.InitializeContext(ctx)
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"card\"><h1>Blazingly fast URL shortener</h1><p>Shorten URLs and track clicks</p><form action=\"/submit\" method=\"post\"><input type=\"url\" id=\"url\" name=\"url\" placeholder=\"Enter URL here...\"> <button type=\"submit\">Shorten URL</button></form></div>")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"card\"><h1>Blazingly fast URL shortener</h1><p>Shorten URLs and track clicks</p><form action=\"/submit\" method=\"post\"><input type=\"text\" id=\"url\" name=\"url\" placeholder=\"Enter URL here...\"> <button type=\"submit\">Shorten URL</button></form></div>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package views
|
package views
|
||||||
|
|
||||||
templ Submission(url string){
|
templ Submission(url, baseurl string){
|
||||||
|
|
||||||
@Base() {
|
@Base() {
|
||||||
<h1>Your url is: {url}</h1>
|
<h1>Your url is: <code>{baseurl + "/" + url}</code></h1>
|
||||||
|
|
||||||
<form action="/stats" method="post">
|
<form action="/stats" method="post">
|
||||||
<input type="hidden" id="id" name="id" value={url}>
|
<input type="hidden" id="id" name="id" value={url}>
|
||||||
|
@ -8,7 +8,7 @@ package views
|
|||||||
import "github.com/a-h/templ"
|
import "github.com/a-h/templ"
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
func Submission(url string) templ.Component {
|
func Submission(url, baseurl string) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
@ -41,20 +41,20 @@ func Submission(url string) templ.Component {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
ctx = templ.InitializeContext(ctx)
|
ctx = templ.InitializeContext(ctx)
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h1>Your url is: ")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h1>Your url is: <code>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var3 string
|
var templ_7745c5c3_Var3 string
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(url)
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(baseurl + "/" + url)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/submit.templ`, Line: 6, Col: 25}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/submit.templ`, Line: 6, Col: 47}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1><form action=\"/stats\" method=\"post\"><input type=\"hidden\" id=\"id\" name=\"id\" value=\"")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</code></h1><form action=\"/stats\" method=\"post\"><input type=\"hidden\" id=\"id\" name=\"id\" value=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user