Initial Commit
This commit is contained in:
commit
822df7fe35
|
@ -0,0 +1,13 @@
|
||||||
|
Copyright (c) 2020, Adalricus Ovicula <adalricus@inventati.org>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -0,0 +1,12 @@
|
||||||
|
Cortris
|
||||||
|
=======
|
||||||
|
|
||||||
|
Cortris is a simple url shortener
|
||||||
|
|
||||||
|
Database Structure
|
||||||
|
------------------
|
||||||
|
Database Name: cortris
|
||||||
|
|
||||||
|
shortURL: Not Null
|
||||||
|
time: Not Null
|
||||||
|
url: Primary Key & Not Null
|
|
@ -0,0 +1,137 @@
|
||||||
|
// Cortris is a simple url shortener
|
||||||
|
|
||||||
|
// Copyright (c) 2020, Adalricus Ovicula <adalricus@inventati.org>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"html/template"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.HandleFunc("/", indexH).Methods("GET")
|
||||||
|
r.HandleFunc("/short/", sURLH).Methods("POST")
|
||||||
|
r.HandleFunc("/{sURL}/", urlH).Methods("GET")
|
||||||
|
|
||||||
|
r.PathPrefix("/static/").
|
||||||
|
Handler(http.FileServer(http.Dir("./")))
|
||||||
|
|
||||||
|
log.Println("listening on port 8080...")
|
||||||
|
srv := &http.Server{
|
||||||
|
Handler: r,
|
||||||
|
Addr: "127.0.0.1:8080",
|
||||||
|
WriteTimeout: 8 * time.Second,
|
||||||
|
ReadTimeout: 8 * time.Second,
|
||||||
|
}
|
||||||
|
log.Fatal(srv.ListenAndServe())
|
||||||
|
}
|
||||||
|
|
||||||
|
type shortP struct {
|
||||||
|
ShortURL string
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
func indexH(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t, _ := template.ParseFiles("templates/index.html")
|
||||||
|
t.Execute(w, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the POST endpoint
|
||||||
|
func sURLH(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
errChk(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dbL := "/home/adalricus/projects/db/adalricus.db"
|
||||||
|
url := r.FormValue("url")
|
||||||
|
db, err := sql.Open("sqlite3", dbL)
|
||||||
|
errChk(err)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
// Checks if url is already in the database
|
||||||
|
stmt, err := db.Prepare("SELECT shortURL FROM cortris WHERE url = ?")
|
||||||
|
errChk(err)
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
var shortURL string
|
||||||
|
err = stmt.QueryRow(url).Scan(&shortURL)
|
||||||
|
|
||||||
|
// If short url doesn't exist then create it
|
||||||
|
if len(shortURL) == 0 {
|
||||||
|
shortURL = randS(8)
|
||||||
|
tx, err := db.Begin()
|
||||||
|
errChk(err)
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare("INSERT INTO cortris(url, shortURL, time) VALUES(?, ?, ?)")
|
||||||
|
errChk(err)
|
||||||
|
|
||||||
|
defer stmt.Close()
|
||||||
|
_, err = stmt.Exec(url, shortURL, time.Now().UTC())
|
||||||
|
errChk(err)
|
||||||
|
|
||||||
|
tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
p := shortP{
|
||||||
|
ShortURL: shortURL,
|
||||||
|
URL: url}
|
||||||
|
t, _ := template.ParseFiles("templates/short.html")
|
||||||
|
t.Execute(w, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This handles short urls
|
||||||
|
func urlH(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
shortURL := vars["sURL"]
|
||||||
|
|
||||||
|
dbL := "/home/adalricus/projects/db/adalricus.db"
|
||||||
|
db, err := sql.Open("sqlite3", dbL)
|
||||||
|
errChk(err)
|
||||||
|
defer db.Close()
|
||||||
|
stmt, err := db.Prepare("SELECT url FROM cortris WHERE shortURL = ?")
|
||||||
|
errChk(err)
|
||||||
|
|
||||||
|
var url string
|
||||||
|
err = stmt.QueryRow(shortURL).Scan(&url)
|
||||||
|
|
||||||
|
if len(url) != 0 {
|
||||||
|
http.Redirect(w, r, url, http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func errChk(err error) {
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func randS(n int) string {
|
||||||
|
var alphaNum = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||||
|
b := make([]rune, n)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = alphaNum[rand.Intn(len(alphaNum))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1,136 @@
|
||||||
|
html {
|
||||||
|
font-family: "Fira Sans";
|
||||||
|
line-height: 1.5;
|
||||||
|
background-color: #282a2e;
|
||||||
|
color: #c5c8c6;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin: auto;
|
||||||
|
padding: 0;
|
||||||
|
max-width: 80ch;
|
||||||
|
}
|
||||||
|
header,
|
||||||
|
main,
|
||||||
|
footer {
|
||||||
|
margin: 1rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
background-color: #1d1f21;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
blockquote {
|
||||||
|
padding: .5rem 1rem;
|
||||||
|
margin: .8rem 0;
|
||||||
|
color: #c5c8c6;
|
||||||
|
border-left: .25rem solid #282a2e;
|
||||||
|
}
|
||||||
|
@media (min-width: 30em) {
|
||||||
|
blockquote {
|
||||||
|
padding-right: 5rem;
|
||||||
|
padding-left: 1.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
border-radius: 5px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
padding: .25em .5em;
|
||||||
|
font-size: 85%;
|
||||||
|
background-color: #282a2e;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
font-size: .8rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
white-space: pre;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
word-wrap: break-word;
|
||||||
|
background-color: #282a2e;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
color: inherit;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #81a2be;
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 0.1rem dashed #81a2be;
|
||||||
|
}
|
||||||
|
a:visited {
|
||||||
|
color: #b294bb;
|
||||||
|
}
|
||||||
|
a:hover,
|
||||||
|
a:focus {
|
||||||
|
border-bottom: 0.2rem solid #81a2be;
|
||||||
|
}
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
border-bottom: 0.16rem solid #cc6666;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
ul, ol, dl {
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
color: #cc6666;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid #282a2e;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
padding: .25rem .5rem;
|
||||||
|
border: 1px solid #282a2e;
|
||||||
|
}
|
||||||
|
tbody tr:nth-child(odd) td,
|
||||||
|
tbody tr:nth-child(odd) th {
|
||||||
|
background-color: #282a2e;
|
||||||
|
}
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
background-color: #282a2e;
|
||||||
|
color: #c5c8c6;
|
||||||
|
padding: 0.2rem 0.8rem;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Cortris</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link href="./static/style.css" rel="stylesheet">
|
||||||
|
<link rel="icon" href="./static/favicon.png" type="image/png">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<a href="./">index</a>
|
||||||
|
⁘ ⁘
|
||||||
|
cortris
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>Cortris</h1>
|
||||||
|
<p>
|
||||||
|
Cortris is a simple url shortener
|
||||||
|
</p>
|
||||||
|
<form action="./short/" method="post">
|
||||||
|
<h3>URL to shorten</h3>
|
||||||
|
<input type="text" maxlength="256" name="url" required>
|
||||||
|
<input type="submit" name="submit" value="Go!">
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<p>
|
||||||
|
Copyright © 2020 Adalricus Ovicula<br>
|
||||||
|
Cortris is licensed under the ISC License.
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,36 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Cortris</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link href="../static/style.css" rel="stylesheet">
|
||||||
|
<link rel="icon" href="../static/favicon.png" type="image/png">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<a href="../">index</a>
|
||||||
|
⁘ ⁘
|
||||||
|
cortris
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>Cortris</h1>
|
||||||
|
<ul>
|
||||||
|
<li>Short URL: <a href="../{{ .ShortURL }}/">{{ .ShortURL }}</a></li>
|
||||||
|
<li>Original URL: {{ .URL }}</li>
|
||||||
|
</ul>
|
||||||
|
<form action="./" method="post">
|
||||||
|
<h3>URL to shorten</h3>
|
||||||
|
<input type="text" maxlength="256" name="url" required>
|
||||||
|
<input type="submit" name="submit" value="Go!">
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<p>
|
||||||
|
Copyright © 2020 Adalricus Ovicula<br>
|
||||||
|
Cortris is licensed under the ISC License.
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue