Add logout functionality.
[?]
Aug 18, 2020, 6:05 PM
PT4276XCOP5NJ3GRFJLIBZKVNVAOATAY5PLWV7FWK6RZW5FTEP5ACDependencies
- [2]
WRPIYG3EUse project listing functionality to check for whether we have a cookie. - [3]
O722AOKEAdd route to allow crediting of events to users/projects. - [4]
I2KHGVD4Require project permissions for access to most data. - [5]
ARX7SHY5Begin work on login UI. - [6]
IZEVQF62Work in progress replacing sqlite with postgres. - [7]
NEDDHXUKReformat via stylish-haskell - [8]
O5FVTOM6Undo JSON silliness, enable a couple more routes. - [9]
HO2PFRABClient login now handles response correctly. - [10]
GMYPBCWEMake docker-compose work. - [11]
2G3GNDDUEvent logging is now functioning in postgres. - [12]
TUA4HMUDUse real API capability for login. - [13]
2XQD6KKKAdd invitation logic and clean up DBProg error handling. - [14]
HMDM3B55Implement core of payments/billing infrastructure. - [15]
EA5BFM5GSplit Login component into its own module. - [16]
JXG3FCXYUpgrade ps + halogen versions. - [17]
PBD7LZYQPostgres & auth are beginning to function. - [18]
TKGBRIQTLogin component now raises LoginComplete message. - [19]
BFZN4SUAMake timeline component work. - [20]
3LMXT7Z6preventDefault on login form submission. - [21]
IPG33FAWAdd billing daemon - [22]
NJNMO72SAdd zcash.com submodule and update client to modern halogen. - [23]
B6HWAPDPModularize & update to recent haskoin. - [24]
EFSXYZPOAutoformat everything with brittany. - [*]
RB2ETNIFAdd skeletal PureScript client project. - [*]
4U7F3CPITHE GREAT RENAMING OF THINGS! - [*]
ADMKQQGCInitial empty Snap project.
Change contents
- edit in client/dev/index.html at line 7
<link rel="stylesheet" type="text/css" href="./assets/css/spinner.css" /> - edit in client/src/Aftok/Login.purs at line 9
import Data.HTTP.Method (Method(POST)) - replacement in client/src/Aftok/Login.purs at line 13
import Affjax (request, defaultRequest, printError)import Affjax (post, get, printError) - edit in client/src/Aftok/Login.purs at line 16
import Affjax.ResponseFormat as RF - edit in client/src/Aftok/Login.purs at line 56
, checkLogin :: m LoginResponse, logout :: m Unit - replacement in client/src/Aftok/Login.purs at line 178
result <- request $defaultRequest { method = Left POST, url = "/api/login", content = Just <<< RB.Json <<< encodeJson $ { username: user, password : pass }}result <- post RF.ignore "/api/login" (Just <<< RB.Json <<< encodeJson $ { username: user, password : pass }) - replacement in client/src/Aftok/Login.purs at line 180
Left err -> log (printError err)Right r -> log ("Got status: " <> show r.status)Left err -> log ("Login failed: " <> printError err)Right r -> log ("Login status: " <> show r.status) - edit in client/src/Aftok/Login.purs at line 188
checkLogin :: Aff LoginResponsecheckLogin = dolog "Sending login check to /api/login/check ..."result <- get RF.ignore "/api/login/check"case result ofLeft err -> dolog ("Login failed: " <> printError err)pure $ Error { status: Nothing, message: printError err }Right r -> dolog ("Login status: " <> show r.status)pure $ case r.status ofStatusCode 200 -> OKStatusCode _ -> Forbidden - edit in client/src/Aftok/Login.purs at line 203
logout :: Aff Unitlogout = dolog "Logging out on server with /api/logout ..."result <- get RF.ignore "/api/logout"case result ofLeft err -> log ("Logout failed: " <> printError err)Right r -> log ("Logout status: " <> show r.status) - replacement in client/src/Aftok/Login.purs at line 212
apiCapability = { login }apiCapability = { login, checkLogin, logout } - replacement in client/src/Aftok/Login.purs at line 215
mockCapability = { login: \_ _ -> pure OK }[3.493]mockCapability ={ login: \_ _ -> pure OK, checkLogin: pure OK, logout: pure unit} - edit in client/src/Main.purs at line 7
import Data.Either (Either(..)) - edit in client/src/Main.purs at line 15
import Halogen.VDom.Driver (runUI)import Halogen.HTML.Core (ClassName(..)) - replacement in client/src/Main.purs at line 19
import Halogen.VDom.Driver (runUI)-- import Halogen.HTML.CSS as CSSimport Halogen.HTML.Events as Eimport Halogen.HTML.Properties as P - edit in client/src/Main.purs at line 26
-- import Effect.Class.Console (info) - replacement in client/src/Main.purs at line 34
mainc = component login timeline projectrunUI mainc unit bodymainComponent = component login timeline projectrunUI mainComponent unit body - replacement in client/src/Main.purs at line 38
= LoggedIn= Loading - edit in client/src/Main.purs at line 40
| LoggedIn - edit in client/src/Main.purs at line 45
| Logout - edit in client/src/Main.purs at line 74
Loading ->HH.div [P.classes [ClassName "loader"]] [HH.text "Loading..."] - replacement in client/src/Main.purs at line 78
HH.div_ [ HH.slot _login unit (Login.component loginCap) unit (Just <<< LoginComplete) ]HH.div_[ HH.slot _login unit (Login.component loginCap) unit (Just <<< LoginComplete) ] - replacement in client/src/Main.purs at line 82
HH.div_ [ HH.slot _timeline unit (Timeline.component tlCap { width: 600.0 }) unit absurd ]withNavBar $ HH.div_[ HH.slot _timeline unit (Timeline.component tlCap { width: 600.0 }) unit absurd ] - replacement in client/src/Main.purs at line 88
projects <- lift pCap.listProjectscase projects ofLeft err -> H.put LoggedOutRight _ -> H.put LoggedInresult <- lift loginCap.checkLogincase result ofLogin.Forbidden -> H.put LoggedOut_ -> H.put LoggedIn - replacement in client/src/Main.purs at line 95[2.4480]
Logout -> dolift loginCap.logoutH.put LoggedOutwithNavBar :: forall s m. H.ComponentHTML MainAction s m -> H.ComponentHTML MainAction s mwithNavBar body =HH.div_[HH.nav[P.classes (ClassName <$> ["navbar", "navbar-expand-lg", "navbar-light", "bg-white"])][HH.div[P.classes (ClassName <$> ["container-fluid"])][brand, logout]],body]brand :: forall a s m. H.ComponentHTML a s mbrand = HH.div[P.classes (ClassName <$> ["navbar-brand"])][HH.h4[P.classes (ClassName <$> ["font-weight-bold"])][HH.text "Aftok"]]logout :: forall s m. H.ComponentHTML MainAction s mlogout = HH.button[P.classes (ClassName <$> ["btn", "navbar-btn", "btn-sm", "btn-primary", "lift", "ml-auto"]),E.onClick \_ -> Just Logout][HH.text "Logout"]-- <!-- Navigation -->-- <ul class="navbar-nav ml-auto">-- <li class="nav-item dropdown">-- <a class="nav-link dropdown-toggle" id="navbarAccount" data-toggle="dropdown" href="#" aria-haspopup="true" aria-expanded="false">-- Guidebook-- </a>-- <ul class="dropdown-menu" aria-labelledby="navbarAccount">-- <li class="dropdown-item dropright">-- <a class="dropdown-link dropdown-toggle" data-toggle="dropdown" href="#">-- Getting Started-- </a>-- <div class="dropdown-menu">-- <a class="dropdown-item" href="@@webRoot/guide-foundation.html">-- Foundational Principles-- </a>-- <a class="dropdown-item" href="@@webRoot/guide-time.html">-- Measuring Contributions-- </a>-- <a class="dropdown-item" href="@@webRoot/guide-revenue.html">-- Revenue Sharing-- </a>-- <a class="dropdown-item" href="@@webRoot/guide-tithing.html">-- Varying Compensation-- </a>-- </div>-- </li>-- <!---- <li class="dropdown-item dropright">-- <a class="dropdown-link dropdown-toggle" data-toggle="dropdown" href="#">-- Up And Running-- </a>-- <div class="dropdown-menu">-- <a class="dropdown-item" href="@@webRoot/guide-voting.html">-- Share-Weighted Voting-- </a>-- <a class="dropdown-item" href="@@webRoot/guide-auctions.html">-- Expense Auctions-- </a>-- <a class="dropdown-item" href="@@webRoot/guide-forks.html">-- Project Forks & Merges-- </a>-- </div>-- </li>-- <li class="dropdown-item dropright">-- <a class="dropdown-link dropdown-toggle" data-toggle="dropdown" href="#">-- Coming Soon!-- </a>-- <div class="dropdown-menu">-- <a class="dropdown-item" href="@@webRoot/guide-debt-contracts.html">-- 3rd-party Contracts-- </a>-- <a class="dropdown-item" href="@@webRoot/guide-delegative-voting.html">-- Delegative Voting-- </a>-- </div>-- </li>-- <li class="dropdown-item dropright">-- <a class="dropdown-item" href="@@webRoot/blog.html">-- Blog-- </a>-- </li>-- -->-- </ul>-- </li>-- <li class="nav-item dropdown">-- <a class="nav-link dropdown-toggle" id="navbarAccount" data-toggle="dropdown" href="#" aria-haspopup="true" aria-expanded="false">-- My Account-- </a>-- <ul class="dropdown-menu" aria-labelledby="navbarAccount">-- <li class="dropdown-item dropright">-- <a class="dropdown-item" data-toggle="modal" href="#modalSigninHorizontal">-- Sign In-- </a>-- </li>-- <!---- <li class="dropdown-item dropright">-- <a class="dropdown-item" href="@@webRoot/account-revenue.html">-- Revenue Dashboard-- </a>-- </li>-- <li class="dropdown-item dropright">-- <a class="dropdown-item" href="@@webRoot/account-auctions.html">-- Active Expense Auctions-- </a>-- </li>-- <li class="dropdown-item dropright">-- <a class="dropdown-item" href="@@webRoot/account-general.html">-- Project List-- </a>-- </li>-- <li class="dropdown-item dropright">-- <a class="dropdown-item" href="@@webRoot/account-tithes.html">-- My Tithes-- </a>-- </li>-- <li class="dropdown-item dropright">-- <a class="dropdown-item" href="@@webRoot/account-keys.html">-- Manage Payment Keys-- </a>-- </li>-- <li class="dropdown-item dropright">-- <a class="dropdown-item" href="@@webRoot/account-billing.html">-- Billing Center-- </a>-- </li>-- -->-- </ul>-- </li>-- <li class="nav-item dropdown">-- <a class="nav-link dropdown-toggle" id="navbarDocumentation" data-toggle="dropdown" href="#" aria-haspopup="true" aria-expanded="false">-- Documentation-- </a>-- <div class="dropdown-menu dropdown-menu-md" aria-labelledby="navbarDocumentation">-- <div class="list-group list-group-flush">-- <a class="list-group-item" href="@@webRoot/docs/index.html">---- <!-- Icon -->-- <div class="icon icon-sm text-primary">-- @@include("../assets/img/icons/duotone-icons/General/Clipboard.svg")-- </div>---- <!-- Content -->-- <div class="ml-4">---- <!-- Heading -->-- <h6 class="font-weight-bold text-uppercase text-primary mb-0">-- Documentation-- </h6>---- <!-- Text -->-- <p class="font-size-sm text-gray-700 mb-0">-- CLI & API user guide-- </p>---- </div>---- </a>-- <a class="list-group-item" href="@@webRoot/faq.html">---- <!-- Icon -->-- <div class="icon icon-sm text-primary">-- @@include("../assets/img/icons/duotone-icons/Code/Question-circle.svg")-- </div>---- <!-- Content -->-- <div class="ml-4">---- <!-- Heading -->-- <h6 class="font-weight-bold text-uppercase text-primary mb-0">-- FAQ-- </h6>---- <!-- Text -->-- <p class="font-size-sm text-gray-700 mb-0">-- Common problems & solutions-- </p>---- </div>---- </a>-- <a class="list-group-item" href="https://discord.gg/wbhCGjw" target="_blank">---- <!-- Icon -->-- <div class="icon icon-sm text-primary">-- @@include("../assets/img/icons/social/discord.svg")-- </div>---- <!-- Content -->-- <div class="ml-4">---- <!-- Heading -->-- <h6 class="font-weight-bold text-uppercase text-primary mb-0">-- Community-- </h6>---- <!-- Text -->-- <p class="font-size-sm text-gray-700 mb-0">-- Join our Discord chat-- </p>---- </div>---- </a>-- <!---- <a class="list-group-item" href="@@webRoot/docs/changelog.html">---- <div class="icon icon-sm text-primary">-- @@include("../assets/img/icons/duotone-icons/Files/File.svg")-- </div>---- <div class="ml-4">---- <h6 class="font-weight-bold text-uppercase text-primary mb-0">-- Changelog-- </h6>---- <p class="font-size-sm text-gray-700 mb-0">-- Project history-- </p>-- </div>-- </a>-- -->-- </div>-- </div>-- </li>-- </ul> - edit in server/Aftok/Snaplet/Auth.hs at line 1
{-# LANGUAGE RankNTypes #-}{-# LANGUAGE TypeApplications #-} - edit in server/Aftok/Snaplet/Auth.hs at line 13
import Data.Text ( Text ) - edit in server/Aftok/Snaplet/Auth.hs at line 35
requireLoginWith (const throwChallenge)requireLoginWith :: (forall a. () -> S.Handler App App a) -> S.Handler App App AU.AuthUserrequireLoginWith throwMissingAuth = do - replacement in server/Aftok/Snaplet/Auth.hs at line 40
rawHeader <- maybe throwChallenge pure $ getHeader "Authorization" reqrawHeader <- maybe (throwMissingAuth ()) pure $ getHeader "Authorization" req - replacement in server/Aftok/Snaplet/Auth.hs at line 60
maybe requireLogin pure currentUsermaybe (requireLoginWith $ const (throwDenied $ AU.AuthError "Not Authenticated")) pure currentUser - replacement in server/Aftok/Snaplet/Auth.hs at line 66
(snapError 403 "Unable to retrieve user record for authenticated user")(snapError 500 "Unable to retrieve user record for authenticated user") - replacement in server/Aftok/Snaplet/Auth.hs at line 80
writeText $ "Access Denied: " <> show failurelogError (encodeUtf8 $ "Access Denied: " <> show @Text failure) - edit in server/Main.hs at line 34
import qualified Snap.Snaplet.Auth as AU - replacement in server/Main.hs at line 49
sesss <- nestSnaplet "sessions" sess $ initCookieSessionManager(cfg ^. authSiteKey . to encodeString)"quookie"(Just "aftok.com")(cfg ^. cookieTimeout)let cookieKey = cfg ^. authSiteKey . to encodeStringsesss <- nestSnaplet "sessions" sess $ initCookieSessionManager cookieKey "quookie" Nothing (cfg ^. cookieTimeout) - replacement in server/Main.hs at line 57
loginRoute = method GET requireLogin >> redirect "/home"loginRoute = method GET requireLogin >> redirect "/app" - edit in server/Main.hs at line 59
checkLoginRoute = void $ method GET requireUserlogoutRoute = method GET (with auth AU.logout) - edit in server/Main.hs at line 110
, ("logout" , logoutRoute), ("login/check" , checkLoginRoute)