Compare commits

..

9 Commits

Author SHA1 Message Date
4rkal
d7f09c07dd Merge branch 'main' of https://github.com/4rkal/shortr 2024-09-12 22:10:41 +03:00
4rkal
6dbe9aa2ce change to shortr from shortr-app on docker-compose 2024-09-12 22:10:17 +03:00
21518a691f
Update README.md 2024-09-12 22:02:10 +03:00
4rkal
620306b6ae added root url 2024-09-12 21:59:49 +03:00
b12d275fbf
Update README.md 2024-09-12 21:46:11 +03:00
04dc9ae612
Update README.md 2024-09-12 21:45:22 +03:00
4rkal
c1fc3dd16e docker-compose.yml 2024-09-12 21:44:31 +03:00
4rkal
77e78d0373 docker 2024-09-12 21:34:38 +03:00
4rkal
64dcf4614e fixed url validation 2024-09-12 21:10:09 +03:00
9 changed files with 111 additions and 13 deletions

5
.dockerignore Normal file
View File

@ -0,0 +1,5 @@
/tmp
*.log
.git
.gitignore
README.md

25
Dockerfile Normal file
View 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"]

View File

@ -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`

View File

@ -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
View 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"]

View File

@ -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>

View File

@ -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
} }

View File

@ -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}>

View File

@ -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
} }