mirror of
https://github.com/jesperh1/forkort.dk.git
synced 2025-05-16 17:28:09 +01:00
Init
This commit is contained in:
parent
e4d1ddb69b
commit
01ae499eba
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# If you prefer the allow list template instead of the deny list, see community template:
|
||||||
|
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||||
|
#
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
|
||||||
|
# Config file
|
||||||
|
config.toml
|
233
assets/style.css
Normal file
233
assets/style.css
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
body {
|
||||||
|
background-color: #202224;
|
||||||
|
/* color: #dbdbdb; */
|
||||||
|
color: #FFF ;
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
margin-bottom: 200px ;
|
||||||
|
/* max-width:650px; */
|
||||||
|
line-height:1.6;
|
||||||
|
font-size:18px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
.active {
|
||||||
|
background-color: #292C2E;
|
||||||
|
}
|
||||||
|
li
|
||||||
|
{
|
||||||
|
float: left;
|
||||||
|
/*display: inline;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
li a {
|
||||||
|
display: block;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
padding: 14px 16px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Change the link color to #111 (black) on hover */
|
||||||
|
li a:hover {
|
||||||
|
background-color: #111;
|
||||||
|
}
|
||||||
|
.main {
|
||||||
|
margin: auto ;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.main svg {
|
||||||
|
vertical-align: -.3em;
|
||||||
|
margin-right: .3rem;
|
||||||
|
-webkit-filter: invert(100%);
|
||||||
|
filter: invert(100%)
|
||||||
|
}
|
||||||
|
.main svg:hover {
|
||||||
|
filter: invert(0%)
|
||||||
|
}
|
||||||
|
|
||||||
|
p img, li img, h1 img, h2 img, h3 img, h4 img {
|
||||||
|
vertical-align: middle ;
|
||||||
|
max-width: 1em;
|
||||||
|
max-height: 1em;
|
||||||
|
border: none ;
|
||||||
|
display: inline ;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 90% ;
|
||||||
|
margin: auto ;
|
||||||
|
display: block ;
|
||||||
|
border: solid 5px beige ;
|
||||||
|
}
|
||||||
|
.titleimg {
|
||||||
|
border: none ;
|
||||||
|
height: 150px ;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center ;
|
||||||
|
color: lightgreen ;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h1 {
|
||||||
|
font-size: 40px ;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: center ;
|
||||||
|
color: deeppink ;
|
||||||
|
font-variant: small-caps;
|
||||||
|
font-size: 24pt ;
|
||||||
|
border-bottom: dashed #ddd 1px ;
|
||||||
|
max-width: 500px ;
|
||||||
|
margin: 1em auto ;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
text-align: center ;
|
||||||
|
font-variant: small-caps ;
|
||||||
|
clear: both ;
|
||||||
|
padding: 2em 0 ;
|
||||||
|
}
|
||||||
|
footer li {
|
||||||
|
display: inline-block ;
|
||||||
|
padding: 0 .5em ;
|
||||||
|
font-size: x-large ;
|
||||||
|
}
|
||||||
|
footer li:hover {
|
||||||
|
background: lightblue ;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer { font-size: large ; }
|
||||||
|
|
||||||
|
h1,h2,h3{
|
||||||
|
font-family: Arial;
|
||||||
|
}
|
||||||
|
|
||||||
|
a{
|
||||||
|
color:royalblue;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
a:hover{color:lightblue;}
|
||||||
|
|
||||||
|
code {
|
||||||
|
color: lime ;
|
||||||
|
border-radius: 5px ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
aside {
|
||||||
|
border: solid 1px black ;
|
||||||
|
border-radius: 20px ;
|
||||||
|
padding: 0 1em 0 1em ;
|
||||||
|
font-size: small ;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside p {
|
||||||
|
color: gray ;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside code {
|
||||||
|
color: green ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .callout here is refencing any aside given the class name callout
|
||||||
|
* An example being: <aside class="callout"> */
|
||||||
|
aside.callout {
|
||||||
|
border: solid 1px orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cnp {
|
||||||
|
width: 100% ;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.cryptocontainer {
|
||||||
|
display: flex ;
|
||||||
|
flex-wrap: wrap ;
|
||||||
|
justify-content: center ;
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* This "@media" block defines rules that will only be applied when the minimum
|
||||||
|
* width of the screen is 55em or greater. In essence, they are settings that
|
||||||
|
* only apply on normal weide screens, but *not* phones and low res monitors.
|
||||||
|
* Since we have more room on widescreens, we change a couple things. */
|
||||||
|
|
||||||
|
@media (min-width: 55em) {
|
||||||
|
|
||||||
|
aside {
|
||||||
|
margin: 0 30px 0 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resright, .disappear {
|
||||||
|
display: block ;
|
||||||
|
float: right;
|
||||||
|
padding: 20px ;
|
||||||
|
clear: both ;
|
||||||
|
max-height: 400px ;
|
||||||
|
max-width: 300px ;
|
||||||
|
}
|
||||||
|
|
||||||
|
header { max-width: 900px ; margin: auto;}
|
||||||
|
main { max-width: 850px ; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.ll { font-size: large ; line-height: 1.3em ; max-width: 600px; margin: auto ; }
|
||||||
|
|
||||||
|
/* These settings are for the cryptocurrency donation QR codes and info on the
|
||||||
|
* main page. */
|
||||||
|
.qr { max-width: 150px ; padding: 10px; border: none; }
|
||||||
|
.cryptocontainer {
|
||||||
|
display: flex ;
|
||||||
|
flex-wrap: wrap ;
|
||||||
|
justify-content: center ;
|
||||||
|
}
|
||||||
|
.cryptoinfo {
|
||||||
|
max-width: 350px ;
|
||||||
|
text-align: center ;
|
||||||
|
padding-left: 10px ;
|
||||||
|
padding-right: 10px ;
|
||||||
|
}
|
||||||
|
.cryptoinfo code,.crypto {
|
||||||
|
font-size: small ;
|
||||||
|
overflow-wrap: break-word ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The "Next Article" Button changes color and also has a 👉 automatically
|
||||||
|
* added to its front. */
|
||||||
|
|
||||||
|
@-webkit-keyframes next {
|
||||||
|
0% {color: yellow ;}
|
||||||
|
100% {color: lightblue}
|
||||||
|
}
|
||||||
|
|
||||||
|
.next a {
|
||||||
|
color: inherit ;
|
||||||
|
}
|
||||||
|
|
||||||
|
.next {
|
||||||
|
color: red ;
|
||||||
|
-webkit-animation:next 1s infinite alternate ;
|
||||||
|
font-size: xx-large ;
|
||||||
|
text-align: center ;
|
||||||
|
margin: auto ;
|
||||||
|
display: block ;
|
||||||
|
font-weight: bold ;
|
||||||
|
padding: 1em ;
|
||||||
|
}
|
||||||
|
|
||||||
|
.next:before {
|
||||||
|
content: "👉" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
4
config.toml.example
Normal file
4
config.toml.example
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
MYSQL_DB = "forkort"
|
||||||
|
MYSQL_USER = "forkort"
|
||||||
|
MYSQL_PASS = "Passw0rd"
|
||||||
|
MYSQL_HOST = "127.0.0.1:3306"
|
BIN
forkort.dk
Executable file
BIN
forkort.dk
Executable file
Binary file not shown.
9
go.mod
Normal file
9
go.mod
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module forkort.dk
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/BurntSushi/toml v1.2.0
|
||||||
|
github.com/go-sql-driver/mysql v1.6.0
|
||||||
|
github.com/gorilla/mux v1.8.0
|
||||||
|
)
|
6
go.sum
Normal file
6
go.sum
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
|
||||||
|
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
|
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||||
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
231
main.go
Normal file
231
main.go
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"math/big"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tmpl = template.Must(template.ParseFiles("./sites/index.html"))
|
||||||
|
var statsTmpl = template.Must(template.ParseFiles("./sites/stats.html"))
|
||||||
|
var successTmpl = template.Must(template.ParseFiles("./sites/success.html"))
|
||||||
|
|
||||||
|
type SuccessShortend struct {
|
||||||
|
LongLink string
|
||||||
|
ShortLink string
|
||||||
|
Success bool
|
||||||
|
Error bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateRandomString returns a securely generated random string.
|
||||||
|
// It will return an error if the system's secure random
|
||||||
|
// number generator fails to function correctly, in which
|
||||||
|
// case the caller should not continue.
|
||||||
|
func GenerateRandomString(n int) (string, error) {
|
||||||
|
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
|
||||||
|
ret := make([]byte, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
ret[i] = letters[num.Int64()]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(ret), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleStats(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var TotalSaved string
|
||||||
|
query := `SELECT * FROM TotalSaved`
|
||||||
|
err := db.QueryRow(query).Scan(&TotalSaved)
|
||||||
|
if err != nil {
|
||||||
|
statsTmpl.Execute(w, TotalSaved)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
statsTmpl.Execute(w, TotalSaved)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AboutPage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.ServeFile(w, r, "./sites/about.html")
|
||||||
|
}
|
||||||
|
func UnshortenHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
token := mux.Vars(r)["token"]
|
||||||
|
var unshortenedLink string
|
||||||
|
query := `SELECT oldLink FROM redirects where newLink = BINARY ?`
|
||||||
|
err := db.QueryRow(query, token).Scan(&unshortenedLink)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
fmt.Fprintf(w, "404 not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, unshortenedLink, http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
tmpl.Execute(w, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
link := r.FormValue("userLink")
|
||||||
|
println(link)
|
||||||
|
u, err := url.ParseRequestURI(string(link))
|
||||||
|
if err != nil {
|
||||||
|
tmpl.Execute(w, struct{ Error bool }{Error: true})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no errors Occured
|
||||||
|
s := SuccessShortend{
|
||||||
|
LongLink: u.String(),
|
||||||
|
ShortLink: "",
|
||||||
|
Success: false,
|
||||||
|
Error: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
var token string
|
||||||
|
query := `SELECT newLink FROM redirects where oldLink = ?`
|
||||||
|
err = db.QueryRow(query, string(link)).Scan(&token)
|
||||||
|
if err == nil {
|
||||||
|
s.ShortLink = "forkort.dk/" + token
|
||||||
|
successTmpl.Execute(w, s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
CREATE_TOKEN:
|
||||||
|
token, err = GenerateRandomString(4)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
fmt.Fprintf(w, "Internal Server Error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
query = `SELECT newLink FROM redirects where newLink = BINARY ?`
|
||||||
|
err = db.QueryRow(query, token).Scan(&token)
|
||||||
|
if err == nil {
|
||||||
|
goto CREATE_TOKEN
|
||||||
|
}
|
||||||
|
s.ShortLink = "forkort.dk/" + token
|
||||||
|
_, err = db.Exec(`INSERT INTO redirects(oldLink, newLink) VALUES (?, ?)`, string(link), token)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
tmpl.Execute(w, struct{ Error bool }{Error: true})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
successTmpl.Execute(w, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnshortenApi(w http.ResponseWriter, r *http.Request) {
|
||||||
|
token := mux.Vars(r)["token"]
|
||||||
|
var unshortenedLink string
|
||||||
|
query := `SELECT oldLink FROM redirects where newLink = BINARY ?`
|
||||||
|
err := db.QueryRow(query, token).Scan(&unshortenedLink)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
fmt.Fprintf(w, "404 not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Write([]byte(unshortenedLink))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ShortenApi(w http.ResponseWriter, r *http.Request) {
|
||||||
|
link, err := io.ReadAll(r.Body)
|
||||||
|
defer r.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u, err := url.ParseRequestURI(string(link))
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
fmt.Fprintf(w, "Invalid URL")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println(u)
|
||||||
|
log.Println(string(link))
|
||||||
|
var token string
|
||||||
|
query := `SELECT newLink FROM redirects where oldLink = ?`
|
||||||
|
err = db.QueryRow(query, string(link)).Scan(&token)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Fprintf(w, "forkort.dk/"+token)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
CREATE_TOKEN:
|
||||||
|
token, err = GenerateRandomString(4)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
fmt.Fprintf(w, "Internal Server Error")
|
||||||
|
}
|
||||||
|
query = `SELECT newLink FROM redirects where newLink = BINARY ?`
|
||||||
|
err = db.QueryRow(query, token).Scan(&token)
|
||||||
|
if err == nil {
|
||||||
|
goto CREATE_TOKEN
|
||||||
|
}
|
||||||
|
_, err = db.Exec(`INSERT INTO redirects(oldLink, newLink) VALUES (?, ?)`, string(link), token)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
fmt.Fprintf(w, "Bad request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "forkort.dk/"+token)
|
||||||
|
}
|
||||||
|
|
||||||
|
var db *sql.DB
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
MYSQL_DB string
|
||||||
|
MYSQL_USER string
|
||||||
|
MYSQL_PASS string
|
||||||
|
MYSQL_HOST string
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var cfg Config
|
||||||
|
file, err := os.ReadFile("./config.toml")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("you need a config.toml file")
|
||||||
|
}
|
||||||
|
err = toml.Unmarshal(file, &cfg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err = sql.Open("mysql", cfg.MYSQL_USER+":@("+cfg.MYSQL_HOST+"/"+cfg.MYSQL_DB+"?parseTime=true")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
err = db.Ping()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
log.Println("Database connection established")
|
||||||
|
|
||||||
|
router := mux.NewRouter()
|
||||||
|
router.HandleFunc("/", IndexHandler)
|
||||||
|
router.HandleFunc("/stats", HandleStats)
|
||||||
|
router.HandleFunc("/about", AboutPage)
|
||||||
|
router.HandleFunc("/api/shorten", ShortenApi)
|
||||||
|
router.HandleFunc("/api/unshorten/{token}", UnshortenApi)
|
||||||
|
router.HandleFunc(`/{token}`, UnshortenHandler)
|
||||||
|
router.PathPrefix("/static/").Handler(http.StripPrefix("/static", http.FileServer(http.Dir("./assets"))))
|
||||||
|
|
||||||
|
http.ListenAndServe(":8080", router)
|
||||||
|
}
|
15
sites/about.html
Normal file
15
sites/about.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Hjem</a></li>
|
||||||
|
<li><a href="/stats">Statistikker</a></li>
|
||||||
|
<li class="active"><a href="/about">Omkring</a></li>
|
||||||
|
</ul>
|
||||||
|
<body style="text-align: center;">
|
||||||
|
<h1>Omkring</h1>
|
||||||
|
<br>
|
||||||
|
<p>Denne side blev startet som et skole projekt og var originalt skrevet i PHP men er siden omskrevet i Go</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
15
sites/forms.html
Normal file
15
sites/forms.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!-- forms.html -->
|
||||||
|
{{if .Success}}
|
||||||
|
<h1>Thanks for your message!</h1>
|
||||||
|
{{else}}
|
||||||
|
<h1>Contact</h1>
|
||||||
|
<form method="POST">
|
||||||
|
<label>Email:</label><br />
|
||||||
|
<input type="text" name="email"><br />
|
||||||
|
<label>Subject:</label><br />
|
||||||
|
<input type="text" name="subject"><br />
|
||||||
|
<label>Message:</label><br />
|
||||||
|
<textarea name="message"></textarea><br />
|
||||||
|
<input type="submit">
|
||||||
|
</form>
|
||||||
|
{{end}}
|
29
sites/index.html
Normal file
29
sites/index.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Forkort.dk - Forkort dine links</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
<meta name="description" content="Forkort.dk gør dine lange links korte.">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ul>
|
||||||
|
<li class="active"><a href="/">Hjem</a></li>
|
||||||
|
<li><a href="/stats">Statistikker</a></li>
|
||||||
|
<li><a href="/about">Omkring</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h1> Forkort.dk - Forkort dine links</h1>
|
||||||
|
<h2><i>Altid uden Reklamer og Trackere</i></h2>
|
||||||
|
<div class="main">
|
||||||
|
<form method="POST">
|
||||||
|
<!-- <p>Indtast link du ønsker at forkorte i boksen nedenfor: </p> -->
|
||||||
|
<label>Indtast dit lange link:</label><br />
|
||||||
|
<input type="text" name="userLink" id="userLink" value="">
|
||||||
|
<button type="submit">Indsend</button>
|
||||||
|
</form>
|
||||||
|
{{if .Error}}
|
||||||
|
<p>Indsend et gyldigt fuldt URL med http/https foran linket</p>
|
||||||
|
{{end}}
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
17
sites/stats.html
Normal file
17
sites/stats.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Hjem</a></li>
|
||||||
|
<li class="active"><a href="/stats">Statistikker</a></li>
|
||||||
|
<li><a href="/about">Omkring</a></li>
|
||||||
|
</ul>
|
||||||
|
<h1> Statistikker </h1>
|
||||||
|
<br>
|
||||||
|
<div class="main">
|
||||||
|
<p>Total Mængde tegn sparet: {{.}}</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
28
sites/success.html
Normal file
28
sites/success.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<script>
|
||||||
|
function copyToClipboard() {
|
||||||
|
// Copy the text inside the text field
|
||||||
|
navigator.clipboard.writeText("https://" + {{.ShortLink}});
|
||||||
|
|
||||||
|
// Alert the copied text
|
||||||
|
alert("Copied the text: " + copyText.value);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<body>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Hjem</a></li>
|
||||||
|
<li><a href="/stats">Statistikker</a></li>
|
||||||
|
<li><a href="/about">Omkring</a></li>
|
||||||
|
</ul>
|
||||||
|
<h1> Link forkortet </h1>
|
||||||
|
<br>
|
||||||
|
<div class="main">
|
||||||
|
<p>Dit link: <a href="{{.LongLink}}">{{.LongLink}}</a></p>
|
||||||
|
<p>Er blevet forkortet</p>
|
||||||
|
<p>Kort link: <a href="{{.ShortLink}}">{{.ShortLink}} </a><a onclick="copyToClipboard()" href="javascript:void(0);"><svg width="20px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M384 112v352c0 26.51-21.49 48-48 48H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h80c0-35.29 28.71-64 64-64s64 28.71 64 64h80c26.51 0 48 21.49 48 48zM192 40c-13.255 0-24 10.745-24 24s10.745 24 24 24 24-10.745 24-24-10.745-24-24-24m96 114v-20a6 6 0 0 0-6-6H102a6 6 0 0 0-6 6v20a6 6 0 0 0 6 6h180a6 6 0 0 0 6-6z"/></svg></a></p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user