commit f1b18f3b478d15148b8abb5c7dfb95c9c0d33cad
parent 7274560398c3512256e53daca1e57e67c177f308
Author: James Eversole <james@eversole.co>
Date: Sun, 3 Jul 2022 21:48:40 -0500
Updated README with project goals, started prototyping frontend and added route for primary css dist, added reasonable functionality for requesting a password directly via link as well as patching index DOM when requesting HTML stub from /pw
Diffstat:
10 files changed, 211 insertions(+), 43 deletions(-)
diff --git a/README.md b/README.md
@@ -1,19 +1,24 @@
-# Purr - Password Generation and Sharing
+# Purr - Password Generation and Secret Sharing

-Purr is a work-in-progress web application offering customizable password generation and time-limited link sharing features.
+Purr is a work-in-progress web application offering customizable password generation and time-limited sharing of secrets.
## What problems does Purr solve?
1. Generating sufficiently memorable but secure passwords for use with accounts that don't offer better authentication methods.
-2. Sharing passwords or other text secrets with others via email/chat/etc., without disclosing the password itself. As passwords expire after a predefined period, the email/chat history where the information was shared don't become a purr-manent (sorry, **permanent**) vulnerability.
+2. Sharing text secrets with others without disclosing the secret in the message itself. As secrets expire after a predefined period, the email/chat history where the information was shared don't become a purr-manent (sorry, **permanent**) vulnerability.
3. Being really cute compared to the competition.
+## Why should I trust you with my secrets?
+You shouldn't! This is [free and open-source software](https://git.eversole.co/James/Purr/src/branch/main/LICENSE) which you can run on your own hardware. Instructions for deployment are coming soon.
+
## Tech Stack?
1. [Haskell](https://www.haskell.org) and [Scotty](https://github.com/scotty-web/scotty) backend.
-2. [HTMX](https://github.com/bigskysoftware/htmx) and [Alpine.js](https://github.com/alpinejs/alpine) for the frontend.
+2. [HTMX](https://github.com/bigskysoftware/htmx) for the frontend.
3. [MongoDB](https://github.com/mongodb/mongo) database.
-## Why should I trust you with my passwords?
-You shouldn't! This is [free and open-source software](https://git.eversole.co/James/Purr/src/branch/main/LICENSE) which you can run on your own hardware. Instructions for deployment are coming soon.
+## Project Goals
+1. Provide a minimal and clean interface for generating and sharing passwords.
+2. Maintain a clean and organized codebase that can be extended to include more utilities than originally anticipated.
+3. Aim for graceful degradation when JavaScript isn't enabled.
## Development & Support
Please send me an [email](mailto:james@eversole.co) or join the [Support Chat](openpgp4fpr://FEB27223219E8DB3203225350462EA0901FE08F7#a=james%40eversole.co&g=Purr%20Support&x=RbVs8iQCVnf&i=-FuzUDK_RM1&s=KgeGtFFJtkq) in [DeltaChat](https://delta.chat)!
diff --git a/src/Core/Configuration.hs b/src/Core/Configuration.hs
@@ -1,6 +1,6 @@
module Core.Configuration ( main ) where
-import Core.Types
+import Core.Types
import Dhall
diff --git a/src/Core/HTTP.hs b/src/Core/HTTP.hs
@@ -2,11 +2,12 @@ module Core.HTTP ( app ) where
import Core.Types
-import Core.Templates (renderIndex)
+import Core.Templates (renderIndex, renderStyle)
import Feature.Sharing.HTTP as Sharing
+import Data.Maybe (Maybe (Nothing))
import Network.Wai.Middleware.RequestLogger (logStdoutDev)
-import Web.Scotty.Trans
+import Web.Scotty.Trans
app :: PurrApp ()
app = do
@@ -15,7 +16,11 @@ app = do
-- Core Routes
get "/" $ do
- html $ renderIndex
+ html $ renderIndex "/" Nothing
+
+ get "/style.css" $ do
+ setHeader "Content-Type" "text/css"
+ text renderStyle
-- Feature Routes
Sharing.routes
diff --git a/src/Core/Templates.hs b/src/Core/Templates.hs
@@ -1,16 +1,19 @@
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
-module Core.Templates ( renderIndex ) where
-
-import qualified Data.Text.Lazy as LT
+module Core.Templates ( renderIndex, renderStyle ) where
+import Data.Text.Lazy (Text)
import Database.MongoDB (Document)
import Text.Blaze.Html.Renderer.Text (renderHtml)
import Text.Blaze.Html
+import Text.Cassius (cassiusFile, renderCss)
import Text.Hamlet (shamletFile)
import Prelude
-renderIndex :: LT.Text
-renderIndex = renderHtml ( $(shamletFile "./views/index.hamlet") )
+renderIndex :: String -> Maybe String -> Text
+renderIndex link password = renderHtml ( $(shamletFile "./views/index.hamlet") )
+
+renderStyle :: Text
+renderStyle = renderCss ( $(cassiusFile "./views/cassius/style.cassius") "/style.css" )
diff --git a/src/Feature/Sharing/HTTP.hs b/src/Feature/Sharing/HTTP.hs
@@ -1,9 +1,10 @@
module Feature.Sharing.HTTP ( routes ) where
-import Core.Types
+import Core.Types
+import Core.Templates (renderIndex)
-import Feature.Sharing.Templates
-import qualified Feature.Sharing.Mongo as DB
+import Feature.Sharing.Templates (renderPw)
+import qualified Feature.Sharing.Mongo as DB
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
@@ -12,21 +13,21 @@ import Control.Monad.Reader (ask, lift)
import Data.AesonBson (aesonify)
import Data.Bson (Document, Field (..), Value (..), lookup)
import Web.Scotty.Trans
-import Prelude hiding (id, lookup)
+import Prelude hiding (lookup)
routes :: PurrApp ()
routes = do
get "/pw/:id" $ do
- id <- param "id"
- doc <- DB.findByLink id
- html $ renderPw id (pwLookup doc)
+ reqId <- param "id"
+ doc <- DB.findByLink reqId
+ html $ renderIndex reqId (pwLookup doc)
post "/pw" $ do
- id <- param "userLink"
- doc <- DB.findByLink id
- html $ renderPw id (pwLookup doc)
+ reqId <- param "userLink"
+ doc <- DB.findByLink reqId
+ html $ renderPw reqId (pwLookup doc)
pwLookup :: Maybe Document -> Maybe String
-pwLookup (Just x) = (lookup "password" x)
-pwLookup _ = Nothing
+pwLookup (Just x) = lookup "password" x
+pwLookup _ = Nothing
diff --git a/src/Lib.hs b/src/Lib.hs
@@ -1,23 +1,23 @@
module Lib ( main ) where
-import Core.Types
import qualified Core.Configuration as Configuration
import qualified Core.HTTP as HTTP
import qualified Core.Mongo as DB
+import Core.Types
import Control.Monad.Reader (liftIO, runReaderT)
import Database.MongoDB (MongoContext)
import GHC.Natural (popCountNatural)
-import Web.Scotty.Trans (scottyT)
import Prelude hiding (id)
+import Web.Scotty.Trans (scottyT)
main :: IO ()
main = do
dhallConf <- liftIO Configuration.main
dataDB <- liftIO $ DB.mongoSetup dhallConf
let config = AppConfig { res = dhallConf
- , dbconn = dataDB
+ , dbconn = dataDB
}
scottyT (port dhallConf) (flip runApp config) HTTP.app where
runApp :: ConfigM a -> AppConfig -> IO a
- runApp m c = runReaderT (runConfigM m) c
+ runApp m = runReaderT (runConfigM m)
diff --git a/stack.yaml b/stack.yaml
@@ -39,6 +39,9 @@ extra-deps:
- AesonBson-0.4.1@sha256:30a4ecb39e8da94dc1e1e8945eb0d4e33a833ae4342841b3c87c56b5918a90a1,1398
- bson-generic-0.0.9@sha256:ea6685daa618b2bbe6e189c33e195e812501baf42f53183eedc16f011690895a,817
+ghc-options:
+ '$everything': -haddock
+
# Override default flag values for local packages and extra-deps
# flags: {}
diff --git a/views/cassius/style.cassius b/views/cassius/style.cassius
@@ -0,0 +1,106 @@
+@colorOne: #222323
+@colorTwo: #f0f6f0
+@colorThree: #6D92AD
+@colorFour: #435F5D
+
+html
+ background-color: #{colorTwo}
+ color: #{colorOne}
+
+body
+ font-family: Courier
+ font-size: 20px
+ text-align: left
+
+p
+ margin: 0.4em 0 0.4em 0
+
+::placeholder
+ color: #{colorOne}
+ opacity: 1
+
+.asciiCat
+ margin: 2% 3% 0 0
+ font-size: 1.2vw
+ color: #{colorFour}
+ text-align: center
+
+.content
+ margin: 0 15% 0 15%
+
+.emptyReq
+ height: 1%
+
+.mainButton
+ padding: 0.75em 1.75em
+ background-color: #{colorThree}
+ color: #{colorTwo}
+ border-style: none
+ text-decoration: none
+
+.mainInput
+ font-weight: 400
+ font-size: 1em
+ width: 50%
+ outline: none
+ color: #{colorOne}
+ background: #{colorTwo}
+ margin: 1em 0
+ border-style: none none none none
+ padding: 0.4em 0
+ box-sizing: border-box
+ -webkit-box-sizing: border-box
+ -moz-box-sizing: border-box
+ -webkit-transition: all 0.1s ease-in-out
+ -moz-transition: all 0.1s ease-in-out
+ -ms-transition: all 0.1s ease-in-out
+ -o-transition: all 0.1s ease-in-out
+ transition: all 0.1s ease-in-out
+
+.mainInput:focus
+ border-bottom: 0.2em solid #{colorThree};
+
+.mainInput:focus::placeholder
+ color: #{colorThree}
+ opacity: 0.5
+
+.pwResult
+ font-size: 1.5em
+ color: #{colorFour}
+
+.pwUtils
+ width: 75%
+
+.shareNew
+ margin-bottom: 2em
+
+.title
+ font-size: 2em
+ color: #{colorOne}
+ text-decoration: none
+
+.titleLink
+ all: unset
+ cursor: pointer
+
+.title h1
+ margin: 0.1em 0 0.3em 0
+
+@media only screen and (max-width : 768px)
+ body
+ text-align: center
+ font-size: 16px
+
+ .asciiCat
+ display: none
+
+ .mainInput
+ width: 95%
+ text-align: center
+
+ .title
+ margin: 8% auto
+ font-size: 3em
+
+ .pwUtils
+ width: 100%
diff --git a/views/index.hamlet b/views/index.hamlet
@@ -1,14 +1,58 @@
+$doctype 5
<html>
<head>
<title>Purr
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/htmx.org@1.7.0" integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous">
+ <link rel="stylesheet" href="/style.css">
+
<body>
- <h1>Welcome to Purr!
- <p #requestedPw>No password currently requested.
- <p>Ask for the <input name="userLink" type="text"/> password
- <button hx-post="/pw"
- hx-target="#requestedPw"
- hx-swap="outerHTML"
- hx-include="[name='userLink']"
- />
- Get Password!
+ <pre #asciiCat .asciiCat>
+ _._ _,-'""`-._
+ \ (,-.`._,'( |\`-/|
+ \ `-.-' \ )-`( , o o)
+ \ `- \`_`"'-
+ <div #content .content>
+ <div #title .title>
+ <h1>
+ <a #titleLink .titleLink href="/">Purr
+ <div #pwUtils .pwUtils>
+ <div #requestedPw .requestedPw>
+ $maybe pw <- password
+ <p>Here's the secret for /pw/#{link}:
+ <h2 .pwResult>#{pw}
+ $nothing
+ $if (link == "/")
+ <p .emptyReq>
+ $else
+ <p>No secret available at /pw/#{link}
+
+ <div #shareNew .shareNew>
+ <p>
+ <input .mainInput
+ name="newPw"
+ type="text"
+ placeholder="Enter a Secret to Share"
+ />
+ <button .mainButton
+ hx-post="/newpw"
+ hx-target="#requestedPw"
+ hx-swap="outerHTML"
+ hx-include="[name='newPw']"
+ />
+ Share Secret
+
+ <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
diff --git a/views/pw.hamlet b/views/pw.hamlet
@@ -1,5 +1,6 @@
-<div #requestedPw>
+<div #requestedPw .requestedPw>
$maybe pw <- password
- <p>Here's the password for https://purr.eversole.co/pw/#{link}: #{pw}
+ <p>Here's the secret for /pw/#{link}:
+ <h2 .pwResult>#{pw}
$nothing
- <p>No password available at https://purr.eversole.co/pw/#{link}
+ <p>No secret available at /pw/#{link}