purr

password generation and secret sharing
Log | Files | Refs | README | LICENSE

commit 117d8793dd18968b4c78501224f0ff067458287f
parent 785a797b7c8083ec7f743555d4e23ae2bc71f443
Author: James Eversole <james@eversole.co>
Date:   Mon, 26 Dec 2022 21:49:21 -0600

Completely overhaul frontend styling for clearer usage. Add support for entering either the full link or just link key for viewing secrets without opening links anew.

Diffstat:
MChangeLog.md | 3+++
MPurr.cabal | 5++++-
Mpackage.yaml | 5+++--
Msrc/Feature/Sharing/HTTP.hs | 12+++++++-----
Msrc/Feature/Sharing/SQLite.hs | 3++-
Mstack.yaml | 3+--
Mviews/cassius/style.cassius | 161+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mviews/gen.hamlet | 47+++++++++++++++++++++++------------------------
Mviews/index.hamlet | 71++++++++++++++++++++++++++++++++++-------------------------------------
Mviews/pw.hamlet | 5++---
10 files changed, 204 insertions(+), 111 deletions(-)

diff --git a/ChangeLog.md b/ChangeLog.md @@ -1,3 +1,6 @@ # Changelog for Purr +0.3.0: + - Complete overhaul of frontend styling + - Properly support entering either the full link or link key only in the "get secret" input field ## Unreleased changes diff --git a/Purr.cabal b/Purr.cabal @@ -14,7 +14,7 @@ license: ISC license-file: LICENSE build-type: Simple extra-source-files: - README.md + README ChangeLog.md library @@ -60,6 +60,7 @@ library , random >=1.2 , scotty ==0.12 , shakespeare >=2.0.20 + , split >=0.2.3.4 , sqlite-simple >=0.4.18.0 , text >=1.2.5.0 , time >=1.9 @@ -97,6 +98,7 @@ executable Purr-exe , random >=1.2 , scotty ==0.12 , shakespeare >=2.0.20 + , split >=0.2.3.4 , sqlite-simple >=0.4.18.0 , text >=1.2.5.0 , time >=1.9 @@ -135,6 +137,7 @@ test-suite Purr-test , random >=1.2 , scotty ==0.12 , shakespeare >=2.0.20 + , split >=0.2.3.4 , sqlite-simple >=0.4.18.0 , text >=1.2.5.0 , time >=1.9 diff --git a/package.yaml b/package.yaml @@ -1,12 +1,12 @@ name: Purr -version: 0.1.0.0 +version: 0.3.0 license: ISC author: "James Eversole" maintainer: "james@eversole.co" copyright: "2022 James Eversole" extra-source-files: -- README.md +- README - ChangeLog.md default-extensions: @@ -42,6 +42,7 @@ dependencies: - scotty == 0.12 - shakespeare >= 2.0.20 - sqlite-simple >= 0.4.18.0 +- split >= 0.2.3.4 - time >= 1.9 - text >= 1.2.5.0 - wai-extra >= 3.1.12.1 diff --git a/src/Feature/Sharing/HTTP.hs b/src/Feature/Sharing/HTTP.hs @@ -9,14 +9,16 @@ import Feature.Sharing.SQLite (findByLink, insertNewSecret) import Feature.Sharing.Templates (renderPw) import Feature.Sharing.Types -import qualified Data.Text as T -import qualified Data.Text.Lazy as LT - import Control.Monad.Reader (ask, lift, liftIO) -import Data.Maybe (listToMaybe) +import Data.Maybe (listToMaybe) +import Data.List.Split (splitOn) import Web.Scotty.Trans import Prelude +import qualified Data.Text as T +import qualified Data.Text.Lazy as LT + + routes :: PurrApp () routes = do @@ -28,7 +30,7 @@ routes = do post "/pw" $ do reqId <- param "userLink" res <- findByLink reqId - html $ renderPw reqId res + html $ renderPw (last $ splitOn "/" reqId) res post "/new" $ do reqSecret <- param "newSec" diff --git a/src/Feature/Sharing/SQLite.hs b/src/Feature/Sharing/SQLite.hs @@ -9,6 +9,7 @@ import Control.Monad.Reader (ask, lift, liftIO) import Crypto.Simple.CBC (encrypt, decrypt) import Data.Maybe (listToMaybe) import Data.Time.Clock.POSIX (getPOSIXTime) +import Data.List.Split (splitOn) import Database.SQLite.Simple import qualified Data.ByteString.Base64 as B64 @@ -22,7 +23,7 @@ findByLink link = do db <- dbPath key <- encKey conn <- liftIO $ open db - res <- liftIO $ query conn "SELECT * from pws WHERE link = ?" (Only link) + res <- liftIO $ query conn "SELECT * from pws WHERE link = ?" (Only (last $ splitOn "/" link)) liftIO $ close conn readEncryptedSecret key res diff --git a/stack.yaml b/stack.yaml @@ -38,8 +38,7 @@ packages: extra-deps: - crypto-simple-0.1.0.0@sha256:5c0e1e04a814d903743d7543245951a91a46817230fdf478fadca57116805fc1,1502 -ghc-options: - '$everything': -haddock +#ghc-options: # Override default flag values for local packages and extra-deps # flags: {} diff --git a/views/cassius/style.cassius b/views/cassius/style.cassius @@ -4,18 +4,23 @@ @colorFour: #435F5D html - font-family: Courier + font-family: Monospace background-color: #{colorTwo} color: #{colorOne} body - margin: 3% 10% 0 10% - font-size: 20px - text-align: left + margin: 1% auto + font-size: 20px + font-weight: 150 + text-align: left header + margin: 0 8% 0 0 text-align: right +a + text-decoration: none + h2 font-family: monaco, Consolas, monospace text-transform: uppercase @@ -29,14 +34,20 @@ p a color: #{colorThree} +.main + margin: 1em auto + max-width: 50% + .title - margin: 0 auto 0 auto - font-size: 2em - color: #{colorThree} + text-align: center + font-size: 2em + margin: 0 auto 0 auto + color: #{colorThree} .titleLink - all: unset - cursor: pointer + font-size: unset + cursor: pointer + font-weight: 100 .title h1 margin: 0.1em 0 0.1em 0 @@ -49,19 +60,22 @@ a opacity: 1 .generators - margin: 0 0 0.5em 0 + padding: 1em 0.5em 0.5em 1.5em + background-color: #{colorTwo} + box-shadow: 8px 8px 12px #ccc -.logo - margin: 4% 3% 0 0 - font-size: 1.2vw - color: #{colorFour} - text-align: center +.generators .numberInput + background-color: #{colorTwo} + +.generatorRadio + margin: 0 -.content - display: flex -.column - flex: 50% +.logo + margin: 4% 3% 0 0 + font-size: 1.2vw + color: #{colorFour} + text-align: center .emptyReq height: 1% @@ -103,7 +117,7 @@ a transition: all 0.1s ease-in-out .mainInput:focus - border-bottom: 0.2em solid #{colorThree}; + border-bottom: 0.2em solid #{colorThree} .mainInput:focus::placeholder color: #{colorThree} @@ -130,7 +144,7 @@ a transition: all 0.1s ease-in-out .numberInput:focus - border-bottom: 0.2em solid #{colorThree}; + border-bottom: 0.2em solid #{colorThree} .numberInput:focus::placeholder color: #{colorThree} @@ -142,33 +156,108 @@ a .genResult color: #{colorFour} +.requestedPw + text-align: center + background-color: #333 + color: #f0f6f0 + margin: 1.5em 0 2em 0 + padding: 0.5em 0.5em 0.5em 1.5em + box-shadow: 8px 8px 12px #ccc + .pwResult - margin: 0 0 1em 0 font-size: 1.5em - color: #{colorFour} + color: #f0f6f0 .shareNew - margin-bottom: 2em + padding: 0.5em 0.5em 0.5em 1.5em + background-color: #{colorTwo} + box-shadow: 8px 8px 12px #ccc + margin: 1em 0 2em 0 + +.shareNew .mainInput + background-color: #{colorTwo} + +.shareNew .numberInput + background-color: #{colorTwo} .requestNew - margin-top: 1em + padding: 0.5em 0.5em 0.5em 1.5em + background-color: #{colorTwo} + box-shadow: 8px 8px 12px #ccc + margin-bottom: 2em + margin-top: 1em -.requestedPw - max-width: 85% +.requestNew .mainInput + background-color: #{colorTwo} .resLink - font-size: 0.75em + font-size: 0.8em .validForm - margin: 0.5em 0 0 0 + margin: 0 + +.radioLabel + display: flex + align-items: center + border-radius: 100px + margin: 0.3em 0 + padding: 0.3em 0.5em + cursor: pointer + transition: .3s + +.radioLabel:hover, .radioLabel:focus-within + background: hsla(0, 0%, 80%, .14) + +.radioInput + position: fixed + left: 0 + top: 0 + width: 1px + height: 1px + opacity: 0 + z-index: -1 + +.radioDesign + width: 22px + height: 22px + border-radius: 100px + background: linear-gradient(to right bottom, #fff, #{colorThree}) + position: relative + +.radioDesign::before + content: '' + display: inline-block + width: inherit + height: inherit + border-radius: inherit + background: hsl(0, 0%, 90%) + transform: scale(1.1) + transition: .3s + +.radioInput:checked+.radioDesign::before + transform: scale(0) + +.radioText + margin-left: 14px + letter-spacing: 3px + transition: .3s + +.radioInput:checked~.radioText + color: hsl(0, 0%, 40%) + +.hidden + display: none @media only screen and (max-width : 768px) body text-align: center font-size: 16px - .content - display: block + .header + margin: 0 2% 0 0 + + .main + max-width: 90% .mainButton width: 80% @@ -176,9 +265,6 @@ a .genButton width: 80% - .generators - text-align: left - .mainInput width: 95% max-width: 100% @@ -188,14 +274,17 @@ a width: 25% .title - margin: 4% auto 2% auto - font-size: 3em + margin: 4% auto 2% auto .title h1 + font-size: 2.5em margin-bottom: 0 .requestedPw max-width: 100% + .pwResult + font-size: 1.75em + .shareUtils width: 100% diff --git a/views/gen.hamlet b/views/gen.hamlet @@ -1,28 +1,20 @@ -<div #generators .column> - <h2>Generators - <h3>Random, Classic, XKCD +<div #generators .generators> <form id="genForm"> - <div .generators"> - <input type="radio" id="gibberish" name="newSec" value="#{show genGibberish}"> - <label for="gibberish" .genResult>#{show genGibberish} - <br /> - <input type="radio" id="oldschool" name="newSec" value="#{show genOldschool}"> - <label for="oldschool" .genResult>#{show genOldschool} - <br /> - <input type="radio" id="xkcd" name="newSec" value="#{show genXkcd}"> - <label for="xkcd" .genResult>#{show genXkcd} - <button .genButton - hx-get="/gen" - hx-target="#generators" - hx-swap="outerHTML" - /> - Generate New - <br /> + <div .generatorRadio"> + <label .radioLabel for="gibberish" .genResult> + <input .radioInput type="radio" id="gibberish" name="newSec" value="#{show genGibberish}"> + <div .radioDesign> + <div .radioText>#{show genGibberish} + <label .radioLabel for="oldschool" .genResult> + <input .radioInput type="radio" id="oldschool" name="newSec" value="#{show genOldschool}"> + <div .radioDesign> + <div .radioText>#{show genOldschool} + <label .radioLabel for="xkcd" .genResult> + <input .radioInput type="radio" id="xkcd" name="newSec" value="#{show genXkcd}"> + <div .radioDesign> + <div .radioText>#{show genXkcd} <div .validForm> - <h3>Share Generated Password - Valid for: - <br /> <input .numberInput name="newSecDuration" type="number" @@ -39,12 +31,19 @@ value="20" onkeyup="if (value < 1 || value > 60) { value = 0 }" /> views - <button .genButton + <button .mainButton hx-post="/new" hx-target="#requestedPw" hx-swap="outerHTML" hx-include="[name='newSec']" /> - Share Password + share selected secret <img class="htmx-indicator" src="/loading.svg" /> + <button .mainButton + hx-get="/gen" + hx-target="#generators" + hx-swap="outerHTML" + /> + regenerate + <br /> diff --git a/views/index.hamlet b/views/index.hamlet @@ -11,19 +11,20 @@ $doctype 5 <header> <a href="https://git.eversole.co/purr"> - Made with &#9829; - | <a href="mailto:#{email}">Contact + made with &#9829; + | + <a href="mailto:#{email}">contact - <div #title .title> - <h1> - <a #titleLink .titleLink href="/">purr + <div #main .main> - <div #content .content> + <div #title .title> + <h1> + <a #titleLink .titleLink href="/">purr - <div #shareUtils .column> - <h2>Sharing Tools + + <div #shareUtils> $if (link == "/") - <div #requestedPw .requestedPw> + <div #requestedPw .hidden> <p .emptyReq> $else <div #requestedPw .requestedPw @@ -35,16 +36,31 @@ $doctype 5 > Loading... <img class="htmx-indicator" src="/loading.svg" /> + <div #requestNew .requestNew> + <p> + <input .mainInput + name="userLink" + type="text" + placeholder="enter a link to view secret" + /> + <button .mainButton + hx-post="/pw" + hx-target="#requestedPw" + hx-swap="outerHTML" + hx-include="[name='userLink']" + /> + get secret + <img class="htmx-indicator" src="/loading.svg" /> + <div #shareNew .shareNew> - <h3>Share New Secret <p> <input .mainInput name="newSec" type="text" - placeholder="Enter a Secret to Share" + placeholder="enter a secret to share" /> <div .validForm> - Valid for: + valid for: <br /> <input .numberInput name="newSecDuration" @@ -68,31 +84,12 @@ $doctype 5 hx-swap="outerHTML" hx-include="[id='shareNew']" /> - Share Secret + share secret <img class="htmx-indicator" src="/loading.svg" /> - <div #requestNew .requestNew> - <h3>Lookup - <p> - <input .mainInput - name="userLink" - type="text" - placeholder="Enter a Link to View Secret" - /> - <button .mainButton - hx-post="/pw" - hx-target="#requestedPw" + <div #generators + hx-trigger="load" + hx-get="/gen" + hx-target="#generators" hx-swap="outerHTML" - hx-include="[name='userLink']" - /> - Get Secret - <img class="htmx-indicator" src="/loading.svg" /> - - <div #generators .column> - <h2>Generators - <button .mainButton - hx-get="/gen" - hx-target="#generators" - hx-swap="outerHTML" - /> - Load Generators + > diff --git a/views/pw.hamlet b/views/pw.hamlet @@ -1,7 +1,6 @@ <div #requestedPw .requestedPw> $maybe pw <- password - <p .resLink>Here's the secret found at <a href="/pw/#{link}">/pw/#{link}</a>: + <p .resLink>secret found at <a href="/pw/#{link}">/pw/#{link}</a>: <h3 .pwResult>#{pw} - <hr /> $nothing - <h3 .pwResult>No secret found at <a href="/pw/#{link}">/pw/#{link}</a> + <h3 .pwResult>no secret found at <a href="/pw/#{link}">/pw/#{link}</a>