Initial commit
This commit is contained in:
commit
55f3f2692d
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
dist
|
||||||
|
cabal-dev
|
||||||
|
*.o
|
||||||
|
*.hi
|
||||||
|
*.chi
|
||||||
|
*.chs.h
|
||||||
|
*.dyn_o
|
||||||
|
*.dyn_hi
|
||||||
|
.virtualenv
|
||||||
|
.hpc
|
||||||
|
.hsenv
|
||||||
|
.cabal-sandbox/
|
||||||
|
cabal.sandbox.config
|
||||||
|
*.prof
|
||||||
|
*.aux
|
||||||
|
*.hp
|
44
Application.hs
Normal file
44
Application.hs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
module Application where
|
||||||
|
|
||||||
|
import Shortener.Common
|
||||||
|
import Shortener.UrlTable
|
||||||
|
|
||||||
|
import Web.Simple
|
||||||
|
import Web.Simple.Static
|
||||||
|
import Web.Simple.Templates
|
||||||
|
import Web.Frank
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad.IO.Class
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Aeson
|
||||||
|
import qualified Data.ByteString.Lazy.Char8 as BL
|
||||||
|
import qualified Data.ByteString.Char8 as BS
|
||||||
|
|
||||||
|
|
||||||
|
app :: (Application -> IO ()) -> IO ()
|
||||||
|
app runner = do
|
||||||
|
settings <- newAppSettings
|
||||||
|
table <- records
|
||||||
|
|
||||||
|
runner $ controllerApp settings $ do
|
||||||
|
get "/" $ render "index.html" ()
|
||||||
|
|
||||||
|
get "/main.css" $ serveStatic "layouts/main.css"
|
||||||
|
|
||||||
|
get "/:word" $ do
|
||||||
|
word <- queryParam' "word"
|
||||||
|
url <- liftIO (extract table word)
|
||||||
|
respond $ case url of
|
||||||
|
Just url -> redirectTo (BS.pack url)
|
||||||
|
Nothing -> notFound
|
||||||
|
|
||||||
|
post "/short" $ do
|
||||||
|
(form, _) <- parseForm
|
||||||
|
case lookup "url" form of
|
||||||
|
Just url -> do
|
||||||
|
address <- return "http://localhost:3000/"
|
||||||
|
word <- liftIO (insert table (BS.unpack url))
|
||||||
|
render "done.html" $ object ["link" .= (address ++ word)]
|
||||||
|
Nothing -> respond badRequest
|
9
Main.hs
Normal file
9
Main.hs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
module Main where
|
||||||
|
|
||||||
|
import Application
|
||||||
|
import Network.Wai.Handler.Warp
|
||||||
|
import Network.Wai.Middleware.RequestLogger
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = app (run 3000 . logStdout)
|
16
Shortener/Common.hs
Normal file
16
Shortener/Common.hs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
|
module Shortener.Common where
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Web.Simple
|
||||||
|
import Web.Simple.Templates
|
||||||
|
|
||||||
|
data AppSettings = AppSettings { }
|
||||||
|
|
||||||
|
newAppSettings :: IO AppSettings
|
||||||
|
newAppSettings = do
|
||||||
|
|
||||||
|
return $ AppSettings
|
||||||
|
|
||||||
|
instance HasTemplates IO AppSettings where
|
||||||
|
defaultLayout = Just <$> getTemplate "layouts/main.html"
|
37
Shortener/Generator.hs
Normal file
37
Shortener/Generator.hs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
module Shortener.Generator
|
||||||
|
( wordID
|
||||||
|
, hashID
|
||||||
|
, Word
|
||||||
|
, Url
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad.State
|
||||||
|
import System.Random
|
||||||
|
import Crypto.Hash.SHA256 (hash)
|
||||||
|
import Data.Binary (decode)
|
||||||
|
import Data.ByteString.Char8 (pack)
|
||||||
|
import Data.ByteString.Lazy (fromStrict)
|
||||||
|
|
||||||
|
type Word = String
|
||||||
|
type Url = String
|
||||||
|
|
||||||
|
-- Choose a random element of a list
|
||||||
|
choice :: [a] -> State StdGen a
|
||||||
|
choice xs = (xs !!) <$> randomSt (0, length xs - 1)
|
||||||
|
where randomSt = state . randomR
|
||||||
|
|
||||||
|
-- Generate a random phonetic string
|
||||||
|
word :: State StdGen Word
|
||||||
|
word = replicateM 10 letter where
|
||||||
|
vowels = "aeiou"
|
||||||
|
consonants = "bcdfghjklmnpqrstvwxyz"
|
||||||
|
letter = choice [vowels, consonants] >>= choice
|
||||||
|
|
||||||
|
-- SHA256 hash to seed a generator
|
||||||
|
hashID :: Url -> Int
|
||||||
|
hashID = decode . fromStrict . hash . pack
|
||||||
|
|
||||||
|
-- Assing a unique word to the url
|
||||||
|
wordID :: Url -> Word
|
||||||
|
wordID = evalState word . mkStdGen . hashID
|
28
Shortener/UrlTable.hs
Normal file
28
Shortener/UrlTable.hs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
module Shortener.UrlTable
|
||||||
|
( UrlTable
|
||||||
|
, records
|
||||||
|
, insert
|
||||||
|
, extract
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Shortener.Generator
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad
|
||||||
|
import Data.Maybe
|
||||||
|
import qualified Data.HashTable.IO as H
|
||||||
|
|
||||||
|
type UrlTable = H.CuckooHashTable Word Url
|
||||||
|
|
||||||
|
--Empty url hash table
|
||||||
|
records :: IO UrlTable
|
||||||
|
records = H.new
|
||||||
|
|
||||||
|
-- Insert the url in the table and return the word
|
||||||
|
insert :: UrlTable -> Url -> IO Word
|
||||||
|
insert table url = H.insert table new url >> return new
|
||||||
|
where new = wordID url
|
||||||
|
|
||||||
|
-- Lookup the table for the associated url
|
||||||
|
extract :: UrlTable -> Word -> IO (Maybe Url)
|
||||||
|
extract = H.lookup
|
67
layouts/main.css
Normal file
67
layouts/main.css
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
@import url(http://fonts.googleapis.com/css?family=Inconsolata:400,700);
|
||||||
|
|
||||||
|
html, body {overflow: auto}
|
||||||
|
body, input {font-size: 1.1em;}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: rgb(24,27,32);
|
||||||
|
color: rgb(155,144,129);
|
||||||
|
font-family: Inconsolata;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
text-align:center;
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container:before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#center {
|
||||||
|
display: inline-block;
|
||||||
|
text-align:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 1em;
|
||||||
|
font-size: 1.8em;
|
||||||
|
font-weight: 700;
|
||||||
|
color: rgb(81,94,102);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
border: none;
|
||||||
|
border-bottom: 2px dashed rgb(43,44,46);
|
||||||
|
outline: none;
|
||||||
|
background: transparent;
|
||||||
|
color: rgb(119,96,73);
|
||||||
|
padding: 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"] {
|
||||||
|
border: 1px solid rgb(43,44,46);
|
||||||
|
padding: .4em;
|
||||||
|
background: transparent;
|
||||||
|
color: rgb(119,87,80);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link, a:visited {
|
||||||
|
color: rgb(119,96,73);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: rgb(119,87,80);
|
||||||
|
text-decoration: underline;
|
||||||
|
transition: color .5s ease;
|
||||||
|
}
|
17
layouts/main.html
Normal file
17
layouts/main.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>url shortener</title>
|
||||||
|
<meta name="description" content="url shortener">
|
||||||
|
<meta name="keywords" content="url, shortener">
|
||||||
|
<meta name="author" content="Michele Guerini Rocco">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel=stylesheet href="main.css" type="text/css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>URL SHORTENER</h1>
|
||||||
|
<div id="container">
|
||||||
|
<div id="center"> $yield$ </div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
18
shortener.cabal
Normal file
18
shortener.cabal
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
name: shortener
|
||||||
|
version: 0.0.1.0
|
||||||
|
author: Rnhmjoj
|
||||||
|
maintainer: micheleguerinirocco@me.com
|
||||||
|
category: Web
|
||||||
|
build-type: Simple
|
||||||
|
cabal-version: >=1.8
|
||||||
|
|
||||||
|
executable shortener
|
||||||
|
main-is: Main.hs
|
||||||
|
ghc-options: -threaded -O2
|
||||||
|
build-depends: base, simple >= 0.8.0,
|
||||||
|
wai, wai-extra,
|
||||||
|
warp, aeson,
|
||||||
|
transformers, mtl,
|
||||||
|
hashtables, cryptohash,
|
||||||
|
bytestring, binary, random,
|
||||||
|
|
1
views/done.html
Normal file
1
views/done.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
here's your new link: <a href="$link$">$link$</a>
|
4
views/index.html
Normal file
4
views/index.html
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<form action="/short" method="POST">
|
||||||
|
your url: <input type="text" name="url">
|
||||||
|
<input type="submit" value="go">
|
||||||
|
</form>
|
Loading…
Reference in New Issue
Block a user