From ad9b9c0f7bd4d95ddc54462970d33d92bab9392c Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Fri, 22 Oct 2021 17:59:44 +0200 Subject: Added a functionning url shortening service --- config.nims | 4 ++ short.nimble | 16 ++++++ src/database.nim | 79 +++++++++++++++++++++++++ src/dbUtils.nim | 13 +++++ src/short.nim | 115 +++++++++++++++++++++++++++++++++++++ src/templates/error.html | 7 +++ src/templates/index.html | 25 ++++++++ src/templates/noshort.html | 10 ++++ src/templates/partials/footer.html | 5 ++ src/templates/partials/master.html | 17 ++++++ src/templates/short.html | 9 +++ static/all.css | 82 ++++++++++++++++++++++++++ static/favicon.ico | Bin 0 -> 1150 bytes tests/database.nim | 41 +++++++++++++ 14 files changed, 423 insertions(+) create mode 100644 config.nims create mode 100644 short.nimble create mode 100644 src/database.nim create mode 100644 src/dbUtils.nim create mode 100644 src/short.nim create mode 100644 src/templates/error.html create mode 100644 src/templates/index.html create mode 100644 src/templates/noshort.html create mode 100644 src/templates/partials/footer.html create mode 100644 src/templates/partials/master.html create mode 100644 src/templates/short.html create mode 100644 static/all.css create mode 100755 static/favicon.ico create mode 100644 tests/database.nim diff --git a/config.nims b/config.nims new file mode 100644 index 0000000..298bb84 --- /dev/null +++ b/config.nims @@ -0,0 +1,4 @@ +switch("define", "release") +switch("define", "flto") +switch("opt", "size") +switch("threads", "on") diff --git a/short.nimble b/short.nimble new file mode 100644 index 0000000..aed9b3a --- /dev/null +++ b/short.nimble @@ -0,0 +1,16 @@ +# Package + +version = "0.1.0" +author = "Julien Dessaux" +description = "A simple, privacy friendly URL shortener" +license = "EUPL-1.2" +srcDir = "src" +bin = @["short"] + + +# Dependencies + +requires "nim >= 1.4.8", + "jester > 0.5.0", + "nimja >= 0.4.1", + "tiny_sqlite > 0.1.2" diff --git a/src/database.nim b/src/database.nim new file mode 100644 index 0000000..8094f31 --- /dev/null +++ b/src/database.nim @@ -0,0 +1,79 @@ +import tiny_sqlite +import std / [options, times] + +import dbUtils + +const migrations = [ + """ + CREATE TABLE schema_version ( + version INTEGER NOT NULL + ); + CREATE TABLE url ( + id INTEGER PRIMARY KEY, + token TEXT NOT NULL UNIQUE, + title TEXT NOT NULL, + url TEXT, + created DATE, + expires DATE + ); + CREATE UNIQUE INDEX idx_url_token ON url(token); + """ +] +const latestVersion = migrations.len + +proc Migrate*(db: DbConn): bool {.raises: [].} = + var currentVersion : int + try: + currentVersion = db.value("SELECT version FROM schema_version;").get().fromDbValue(int) + except SqliteError: + discard + if currentVersion != latestVersion: + try: + db.exec("BEGIN") + for v in currentVersion.. 527040: + echo "exp" + return (Http400, renderError(400, "Bad Request")) + of "shorten": discard + else: return (Http400, renderError(400, "Bad Request")) + if input.Title == "" or input.Url == "" or exp == 0: + echo "empty" + return (Http400, renderError(400, "Bad Request")) + input.Token = $genOid() + input.Created = times.now() + input.Expires = input.Created + initDuration(minutes=exp) + try: + db.AddUrl(input) + except SqliteError: + return (Http500, renderShort(input)) + return (Http200, input.Token) + +routes: + get "/": + resp renderIndex() + post "/": + initDB() + var (code, content) = handleIndexPost(request.params) + if code != Http200: + resp code, content + else: + redirect("/" & content) + get "/static/favicon.ico": + resp Http200, {"content-type": "image/x-icon"}, favicon + get re"^/static/all\.css\.": + resp Http200, {"content-type": "text/css"}, allcss + get "/@token": + initDB() + var (code, content) = handleToken(@"token") + resp code, content + +runForever() diff --git a/src/templates/error.html b/src/templates/error.html new file mode 100644 index 0000000..5de1923 --- /dev/null +++ b/src/templates/error.html @@ -0,0 +1,7 @@ +{% extends "templates/partials/master.html" %} +{% block content %} +

{{ $code }} - {{ $msg }}

+

+ Go back +

+{% endblock %} diff --git a/src/templates/index.html b/src/templates/index.html new file mode 100644 index 0000000..b89e381 --- /dev/null +++ b/src/templates/index.html @@ -0,0 +1,25 @@ +{% extends "templates/partials/master.html" %} +{% block content %} +

URL shortener

+

+The simple, open source and privacy friendly URL shortener : anonymous usage, no tracking.
+This is a personal sharing service: Data may be deleted anytime. Don't share illegal, unethical or morally reprehensible content. +

+
+
+
+ + + +
+ +{% endblock %} diff --git a/src/templates/noshort.html b/src/templates/noshort.html new file mode 100644 index 0000000..e0be436 --- /dev/null +++ b/src/templates/noshort.html @@ -0,0 +1,10 @@ +{% extends "templates/partials/master.html" %} +{% block content %} +

URL not found!

+

+ This url does not exist or has expired, sorry! +

+

+ Go back +

+{% endblock %} diff --git a/src/templates/partials/footer.html b/src/templates/partials/footer.html new file mode 100644 index 0000000..6ae4459 --- /dev/null +++ b/src/templates/partials/footer.html @@ -0,0 +1,5 @@ + diff --git a/src/templates/partials/master.html b/src/templates/partials/master.html new file mode 100644 index 0000000..82cedac --- /dev/null +++ b/src/templates/partials/master.html @@ -0,0 +1,17 @@ + + + + + + + + + short.adyxax.org + + +
+ {% block content %}{% endblock %} +
+ {% importnwt "templates/partials/footer.html" %} + + diff --git a/src/templates/short.html b/src/templates/short.html new file mode 100644 index 0000000..1f36fdf --- /dev/null +++ b/src/templates/short.html @@ -0,0 +1,9 @@ +{% extends "templates/partials/master.html" %} +{% block content %} +

{{ $req.Title }}

+

{{ $req.Url }}

+

+Created on : {{ $req.Created }}
+Expires on : {{ $req.Expires }} +

+{% endblock %} diff --git a/static/all.css b/static/all.css new file mode 100644 index 0000000..3e31e06 --- /dev/null +++ b/static/all.css @@ -0,0 +1,82 @@ +* { + box-sizing: border-box; +} +body { + display: grid; + grid-template-rows: auto 1fr auto; + + font-family: BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + + font-feature-settings: "kern" 1; + font-kerning: normal; +} +#main { + padding-left:1em; + padding-right:1em; +} +p { + text-align: justify; + hyphens: auto; + text-justify: inter-character; + overflow-wrap: anywhere; +} +h1, h2, h3, h4, h5 { + font-family: open, serif; +} +@media only screen and (min-width: 60rem) { + body { + max-width:60rem; + margin-left: auto; + margin-right: auto; + } +} +.fullwidth { + width: 100%; +} +footer { + background-color: #002b36; + padding: 10px; +} +footer p { + color: #859900; + text-align: center; +} +footer a { + color: #859900; +} +html { + background-color: #002b36; + color: #839496; +} +body, main { + background-color: #073642; +} +code { + background-color: #073642; +} +a { + color: #b58900; +} +a:visited { + color: #cb4b16; +} +a:hover { + color: #cb4b16; +} +h1 { + color: #cb4b16; +} +h2, +h3, +h4, +h5, +h6 { + color: #859900; +} +pre { + background-color: #002b36; + color: #839496; +} +pre, code { + background-color: #002b36; +} diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100755 index 0000000..216330f Binary files /dev/null and b/static/favicon.ico differ diff --git a/tests/database.nim b/tests/database.nim new file mode 100644 index 0000000..9e8de52 --- /dev/null +++ b/tests/database.nim @@ -0,0 +1,41 @@ +include ../src/database + +import unittest + +const someTime = initDuration(seconds=1) +let testingNow = times.now() - 60 * someTime +let later = testingNow + 30 * someTime + +suite "database": + test "url": + let db = openDatabase(":memory:") + check db.Migrate() == true + let u = ShortUrl( + Token: "token", + Title: "title", + Url: "url", + Created: testingNow, + Expires: later, + ) + db.AddUrl(u) + try: + db.AddUrl(u) + check false + except SqliteError: + discard + var u2 = db.GetUrl(u.Token) + check u2.ID == 1 + check u2.Token == "token" + check u2.Title == "title" + check u2.Url == "url" + check u2.Created - testingNow < someTime + check u2.Expires - later < someTime + db.CleanExpired() + try: + discard db.GetUrl("token") + except SqliteError: + check false + u2.Expires = testingNow + 120 * someTime + db.AddUrl(u2[]) + db.CleanExpired() + check db.GetUrl("token") != nil -- cgit v1.2.3