Add billing daemon
[?]
Sep 24, 2017, 9:28 PM
IPG33FAWXGEQ2PO6OXRT2PWWXHRNMPVUKKADL6UKKN5GD2CNZ25ACDependencies
- [2]
4B66XH43Add sample billing config - [3]
SOIAMXLWBuild versioned docker images. - [4]
RSEB2NFGReplacing Snap with Scotty. - [5]
2G3GNDDUEvent logging is now functioning in postgres. - [6]
ADMKQQGCInitial empty Snap project. - [7]
7KZP4RHZSwitch from Data.Time to Data.Thyme - [8]
O5FVTOM6Undo JSON silliness, enable a couple more routes. - [9]
FXJQACESEnsure that auction is not ended at the time of bid - [10]
A6HKMINBAttempting to improve JSON handling. - [11]
Z3MK2PJ5Add GET handler for retrieving auction data. - [12]
RN7EI6INUpdate database layer to use CreditTo - [13]
SCXG6TJWMake log reduction safer in presence of overlapping events. - [14]
LD4GLVSFMore database stuff. - [15]
O722AOKEAdd route to allow crediting of events to users/projects. - [16]
DXIGERDTChange order of Docker build to avoid rebuilding the universe. - [17]
Y3LIJ5USAdd handler for CreatePaymentRequest - [18]
O227CEAVAdds storage of original event JSON for some DBOp constructors. - [19]
3QVT6MA6Add database support for event amend operations. - [20]
AL37SVTCImplement payments service endpoints. - [21]
4FDQGIXNMake payment request retrieval key an opaque 32-bit hash. - [22]
WAIX6AGNAdd event serialization for PaymentRequest & Payment - [23]
4U7F3CPITHE GREAT RENAMING OF THINGS! - [24]
64C6AWH6Rename Ananke -> Quixotic, project reboot. - [25]
XTBSG4C7Adding serveJSON combinator to eliminate some boilerplate from handlers. - [26]
ASF3UPJLAdd auction creation and bid handlers - [27]
NTPC7KJETrivial changes, feature scratchpad. - [28]
DFOBMSAOInitial work on payments API - [29]
4ZLEDBK7Initial attempts at dockerizing, cabal isn't cooperating. - [30]
Q5X5RYQLstylish-haskell reformatting - [31]
HMDM3B55Implement core of payments/billing infrastructure. - [32]
MJ6R42RCUtility methods for reading key & cert data. - [33]
V2VDN77HEnable postgres configuration via environment variable for Heroku. - [34]
6L5BK5EHUse generic SMTP rather than Sendmail-specific mail client. - [35]
TNR3TEHKSwitch to Postgres + snaplet arch compiles. - [36]
LEINLS3XUpdate deployment documentation. - [37]
2XQD6KKKAdd invitation logic and clean up DBProg error handling. - [38]
NVOCQVASInitial failing tests. - [39]
4IQVQL4TAdded client for payouts endpoint. - [40]
NLZ3JXLOFix formatting with stylish-haskell. - [41]
W35DDBFYFactor common JSON conversions up into client lib module. - [42]
73NDXDEZBegin implementation of billing event persistence. - [43]
NAS4BFL4Trivial stylish-haskell reformat. - [44]
EW2XN7KUUpdate docker build, clean up migration for payments tables. - [45]
EMVTF2IWWIP moving back to snap. - [46]
GKGVYBZGAdded JSON serialization to TimeLog - [47]
KNSI575VCleanup of EventLog types. - [48]
SPJCFHXWUpdate shell scripts to point to https://aftok.com and prompt for input. - [49]
7HPY3QPFFix linting errors. (yay hlint!) - [50]
SEWTRB6SImplement payment request creation functions. - [51]
QMRKFEPGRefactor QDB to use a free monad algebra instead. - [52]
POX3UAMTEnabling logging of time to contributor/project accounts - [53]
2Y2QZFVFSwitch to more modern cabal2nix-based workflow. - [54]
M3KUPGZKAdd invitation email template. - [55]
UILI6PILThe route-based logStart/logStop is nicer. - [56]
WFZDMVUXRename ADB -> QDB - [57]
NEDDHXUKReformat via stylish-haskell - [58]
Z7KS5XHHVery WIP. Wow. - [59]
HO2PFRABClient login now handles response correctly. - [60]
5OI44E4EAdd authentication to auction search. - [61]
TLQ72DSJLenses, sqlite-simple - [62]
5XFJNUAZStart of addition of project infrastructure. - [63]
RPAJLHMTChange to use UUIDs instead of ints for primary keys. - [64]
WZFQDWW4Add retrieval/storage of current exchange rate data to payment recording. - [65]
KEP5WUFJConvert project to stack-based build. - [66]
QADKFHARAdds CreatePayment handler implementation. - [67]
JFOEOFGAstylish-haskell formatting. - [68]
BSIUHCGFAdd payment response handler. - [69]
MGOF7IUFUpdate TASKS list to reflect completed projects. - [70]
HALRDT2FAdded initial auction create route. - [71]
Y35QCWYWMinor improvement in WorkIndex type to eliminate duplicated information. - [72]
IZEVQF62Work in progress replacing sqlite with postgres. - [73]
BROSTG5KBeginning of modularization of server. - [74]
7VGYLTMUClean up schema version handling. - [75]
LCBJULKEFix swapped default and key in QConfig. - [76]
ZP62WC47Begin conversion to build with stack. - [77]
PBD7LZYQPostgres & auth are beginning to function. - [78]
GCVQD44VCreate amends endpoint, switch to UUID primary keys - [79]
I2KHGVD4Require project permissions for access to most data. - [80]
TCOAKCGGCompleted conversion to snap. - [81]
FD7SV5I6Fix handling of event_t columns. - [*]
EKI57EJRAdd alternative implementation of auction winner determination. - [*]
5DRIWGLUImproving TimeLog specs - [*]
AXKKXBWNInitial attempt at writing down my ideas for a company based on trust. - [*]
2WOOGXDHUse dbmigrations to manage database state.
Change contents
- edit in Dockerfile at line 55
ADD ./daemon /opt/aftok/daemon - edit in Makefile at line 12
- edit in aftok.cabal at line 26
Aftok.Config - edit in aftok.cabal at line 64
, network - edit in aftok.cabal at line 72
, smtp-mail, system-filepath - edit in aftok.cabal at line 81
, x509, x509-store - edit in aftok.cabal at line 138
, Aftok.Snaplet.Billing - edit in aftok.cabal at line 177
, system-filepath, text, thyme, transformers, uuid, wreq, x509, x509-storeExecutable aftok-daemondefault-language: Haskell2010ghc-options: -Wall -Werrorhs-source-dirs: daemondefault-extensions: NoImplicitPrelude, OverloadedStrings, RecordWildCards, ScopedTypeVariables, KindSignaturesmain-is: Main.hsother-modules: AftokD, AftokD.AftokMbuild-depends:aftok, base, aeson, attoparsec, base64-bytestring, bytestring, bippy, cereal, classy-prelude, containers, configurator, cryptonite, either, errors, hourglass, HStringTemplate, iso8601-time, HsOpenSSL, http-client, http-client-openssl, lens, mime-mail, mtl, network, network-uri, optparse-applicative, postgresql-simple, protobuf, smtp-mail, system-filepath - file addition: daemon[85.2]
- file addition: AftokD[0.1418]
- file addition: AftokM.hs[0.1438]
{-# LANGUAGE TemplateHaskell #-}{-# LANGUAGE GeneralizedNewtypeDeriving #-}{-# LANGUAGE RecordWildCards #-}{-# LANGUAGE FlexibleContexts #-}module AftokD.AftokM whereimport ClassyPreludeimport Control.Error.Util (maybeT)import Control.Lens ((^.), makeLenses, makeClassyPrisms, traverseOf, to)import Control.Monad.IO.Class (MonadIO(..))import Control.Monad.Except (MonadError, throwError)import Control.Monad.Reader (MonadReader)import Control.Monad.Trans.Except (ExceptT, withExceptT, runExceptT)import Control.Monad.Trans.Reader (mapReaderT, withReaderT)import Crypto.Random.Types (MonadRandom(..))import Database.PostgreSQL.Simple (Connection, connect)import Data.Thyme.Clock as Cimport Data.Thyme.Time as Timport Network.Mail.Mimeimport Network.Mail.SMTP as SMTPimport Network.URI (URI, parseURI)import Text.StringTemplateimport Filesystem.Path.CurrentOS (encodeString)import Network.Bippy.Types (Satoshi)import Aftok (User, UserId, userEmail, _Email)import Aftok.Types (satoshi)import qualified Aftok.Config as ACimport Aftok.Billables (Billable, Billable', Subscription', customer, name, billable, project, paymentRequestEmailTemplate, paymentRequestMemoTemplate)import qualified Aftok.Database as DBimport Aftok.Database.PostgreSQL (QDBM(..))import qualified Aftok.Payments as Pimport Aftok.Payments.Types (PaymentKey(..), subscription, paymentRequestTotal, paymentKey)import Aftok.Project (Project, ProjectId(..), projectName)import qualified AftokD as Ddata AftokDErr= ConfigError Text| DBErr DB.DBError| PaymentErr P.PaymentErrormakeClassyPrisms ''AftokDErrinstance P.AsPaymentError AftokDErr where_PaymentError = _PaymentErr . P._PaymentError_Overdue = _PaymentErr . P._Overdue_SigningError = _PaymentErr . P._SigningErrordata AftokMEnv = AftokMEnv{ _dcfg :: !D.Config, _conn :: !Connection, _pcfg :: !P.PaymentsConfig}makeLenses ''AftokMEnvinstance P.HasPaymentsConfig AftokMEnv wherenetwork = pcfg . P.networksigningKey = pcfg . P.signingKeypkiData = pcfg . P.pkiDatapaymentsConfig = pcfgnewtype AftokM a = AftokM { runAftokM :: ReaderT AftokMEnv (ExceptT AftokDErr IO) a }deriving (Functor, Applicative, Monad, MonadIO, MonadError AftokDErr, MonadReader AftokMEnv)instance MonadRandom AftokM wheregetRandomBytes = liftIO . getRandomBytesinstance DB.MonadDB AftokM whereliftdb = liftQDBM . DB.liftdbliftQDBM :: QDBM a -> AftokM aliftQDBM (QDBM r) =AftokM . mapReaderT (withExceptT DBErr) . withReaderT _conn $ rcreateAllPaymentRequests :: D.Config -> IO ()createAllPaymentRequests cfg = doconn' <- connect $ cfg ^. D.dbConfigpcfg' <- AC.toPaymentsConfig $ cfg ^. D.billingConfiglet env = AftokMEnv cfg conn' pcfg'void . runExceptT $ (runReaderT . runAftokM) createProjectsPaymentRequests $ envcreateProjectsPaymentRequests :: AftokM ()createProjectsPaymentRequests = doprojects <- liftQDBM $ DB.listProjectstraverse_ createProjectPaymentRequests projectscreateProjectPaymentRequests :: ProjectId -> AftokM ()createProjectPaymentRequests pid = donow <- liftIO C.getCurrentTimelet ops = P.BillingOps memoGen (fmap Just . paymentURL) payloadGensubscribers <- liftQDBM $ DB.findSubscribers pidrequests <- traverse (\uid -> P.createPaymentRequests ops now uid pid) $ subscriberstraverse_ sendPaymentRequestEmail (join requests)sendPaymentRequestEmail :: P.PaymentRequestId -> AftokM ()sendPaymentRequestEmail reqId = docfg <- asklet AC.SmtpConfig{..} = cfg ^. (dcfg . D.smtpConfig)preqCfg = cfg ^. (dcfg . D.paymentRequestConfig)reqMay = dopreq <- DB.findPaymentRequestId reqIdpreq' <- traverseOf P.subscription DB.findSubscriptionBillable preqpreq'' <- traverseOf (P.subscription . customer) DB.findUser preq'traverseOf (P.subscription . billable . project) DB.findProject preq''req <- maybeT (throwError $ DBErr DB.SubjectNotFound) pure reqMaybip70URL <- paymentURL (req ^. paymentKey)mail <- buildPaymentRequestEmail preqCfg req bip70URLlet mailer = maybe (sendMailWithLogin _smtpHost) (sendMailWithLogin' _smtpHost) _smtpPortliftIO $ mailer _smtpUser _smtpPass mailbuildPaymentRequestEmail :: (MonadIO m, MonadError AftokDErr m)=> D.PaymentRequestConfig-> P.PaymentRequest' (Subscription' User (Billable' Project UserId Satoshi))-> URI-> m MailbuildPaymentRequestEmail cfg req paymentUrl = dotemplates <- liftIO . directoryGroup $ encodeString (cfg ^. D.templatePath)let billTemplate = (newSTMP . unpack) <$> req ^. (subscription . billable . paymentRequestEmailTemplate)defaultTemplate = getStringTemplate "payment_request" templatescase billTemplate <|> defaultTemplate ofNothing -> throwError $ ConfigError "Could not find template for invitation email"Just template ->let fromEmail = cfg ^. D.billingFromEmailtoEmail = req ^. (subscription . customer . userEmail)pname = req ^. (subscription . billable . project . projectName)total = req ^. (P.paymentRequest . to paymentRequestTotal)setAttrs = setManyAttrib[ ("from_email", fromEmail ^. _Email), ("project_name", pname), ("to_email", toEmail ^. _Email), ("amount_due", tshow $ total ^. satoshi), ("payment_url", tshow paymentUrl)]fromAddr = Address Nothing ("billing@aftok.com")toAddr = Address Nothing (toEmail ^. _Email)subject = "Payment is due for your "<>pname<>" subscription!"body = plainTextPart . render $ setAttrs templatein pure $ SMTP.simpleMail fromAddr [toAddr] [] [] subject [body]memoGen :: Subscription' UserId Billable-> T.Day-> C.UTCTime-> AftokM (Maybe Text)memoGen sub billingDate requestTime = doreq <- traverseOf (billable . project) DB.findProjectOrError sublet template = (newSTMP . unpack) <$> (sub ^. (billable . paymentRequestMemoTemplate))setAttrs = setManyAttrib[ ("project_name", req ^. (billable . project . projectName)), ("subscription", req ^. (billable . name)), ("billing_date", tshow billingDate), ("issue_time", tshow requestTime)]pure $ fmap (render . setAttrs) template-- The same URL is used for retrieving a BIP-70 payment request and for submitting-- the response.paymentURL :: PaymentKey -> AftokM URIpaymentURL (PaymentKey k) = doenv <- asklet hostname = env ^. (dcfg . D.paymentRequestConfig . D.aftokHost)paymentRequestPath = "https://" <> hostname <> "/pay/" <> kmaybe(throwError . ConfigError $ "Could not parse path " <> paymentRequestPath <> " to a valid URI")pure(parseURI $ show paymentRequestPath)payloadGen :: Monad m => Subscription' UserId Billable -> T.Day -> C.UTCTime -> m (Maybe ByteString)payloadGen _ _ _ = pure Nothing - file addition: AftokD.hs[0.1418]
{-# LANGUAGE TemplateHaskell #-}module AftokD whereimport ClassyPrelude hiding (FilePath)import Control.Lensimport qualified Data.Configurator as Cimport qualified Data.Configurator.Types as CTimport Database.PostgreSQL.Simple (ConnectInfo)import Filesystem.Path.CurrentOS (FilePath, fromText, encodeString)import Aftok (Email(..))import qualified Aftok.Config as ACdata PaymentRequestConfig = PaymentRequestConfig{ _aftokHost :: Text, _templatePath :: FilePath, _billingFromEmail :: Email}makeLenses ''PaymentRequestConfigdata Config = Config{ _smtpConfig :: AC.SmtpConfig, _billingConfig :: AC.BillingConfig, _dbConfig :: ConnectInfo, _paymentRequestConfig :: PaymentRequestConfig}makeLenses ''ConfigloadConfig :: FilePath -> IO ConfigloadConfig cfgFile =readConfig =<< C.load [C.Required $ encodeString cfgFile]readConfig :: CT.Config -> IO ConfigreadConfig cfg = Config<$> (AC.readSmtpConfig $ C.subconfig "smtp" cfg)<*> (AC.readBillingConfig $ C.subconfig "billing" cfg)<*> (AC.readConnectInfo $ C.subconfig "db" cfg)<*> (readPaymentRequestConfig $ C.subconfig "payment_requests" cfg)readPaymentRequestConfig :: CT.Config -> IO PaymentRequestConfigreadPaymentRequestConfig cfg = PaymentRequestConfig<$> C.require cfg "aftok_host"<*> (fromText <$> C.require cfg "template_path")<*> (Email <$> C.require cfg "payment_from_email") - file addition: Main.hs[0.1418]
{-# LANGUAGE TemplateHaskell #-}module Main (main) whereimport ClassyPreludeimport System.Environment (getEnv)import Filesystem.Path.CurrentOS (decodeString)import qualified AftokD as Dimport AftokD.AftokM (createAllPaymentRequests)main :: IO ()main = docfgPath <- try $ getEnv "AFTOK_CFG" :: IO (Either IOError String)cfg <- D.loadConfig . decodeString $ either (const "conf/aftok.cfg") id cfgPathcreateAllPaymentRequests cfg - edit in lib/Aftok/Billables.hs at line 70
, _paymentRequestEmailTemplate :: Maybe Text, _paymentRequestMemoTemplate :: Maybe Text - replacement in lib/Aftok/Billables.hs at line 85
} deriving (Functor, Foldable, Traversable)} - file addition: Config.hs[4.679]
{-# LANGUAGE TemplateHaskell #-}module Aftok.Config whereimport ClassyPrelude hiding (FilePath)import Control.Lens (makeClassy, (^.))import qualified Data.Configurator as Cimport qualified Data.Configurator.Types as CTimport Data.X509import Data.X509.File (readKeyFile, readSignedObject)import Database.PostgreSQL.Simple (ConnectInfo(..))import Filesystem.Path.CurrentOS (FilePath, fromText, encodeString)import qualified Network.Bippy.Types as BTimport qualified Network.Mail.SMTP as SMTPimport qualified Network.Socket as NSimport Aftok.Payments (PaymentsConfig(..))data SmtpConfig = SmtpConfig{ _smtpHost :: NS.HostName, _smtpPort :: Maybe NS.PortNumber, _smtpUser :: SMTP.UserName, _smtpPass :: SMTP.Password}makeClassy ''SmtpConfigdata BillingConfig = BillingConfig{ _network :: BT.Network, _signingKeyFile :: FilePath, _certsFile :: FilePath, _exchangeRateServiceURI :: String}makeClassy ''BillingConfigreadSmtpConfig :: CT.Config -> IO SmtpConfigreadSmtpConfig cfg =SmtpConfig <$> C.require cfg "smtpHost"<*> ((fmap . fmap) fromInteger $ C.lookup cfg "smtpPort")<*> C.require cfg "smtpUser"<*> C.require cfg "smtpKey"readBillingConfig :: CT.Config -> IO BillingConfigreadBillingConfig cfg =BillingConfig <$> (parseNetwork <$> C.require cfg "network")<*> (fromText <$> C.require cfg "signingKeyFile")<*> (fromText <$> C.require cfg "certsFile")<*> C.require cfg "exchangeRateServiceURI"where parseNetwork :: String -> BT.NetworkparseNetwork "main" = BT.MainNetparseNetwork _ = BT.TestNetreadConnectInfo :: CT.Config -> IO ConnectInforeadConnectInfo cfg =ConnectInfo <$> C.require cfg "host"<*> C.require cfg "port"<*> C.require cfg "user"<*> C.require cfg "password"<*> C.require cfg "database"toPaymentsConfig :: BillingConfig -> IO PaymentsConfigtoPaymentsConfig c = doprivKeys <- readKeyFile . encodeString $ c ^. signingKeyFilepkiEntries <- readSignedObject . encodeString $ c ^. certsFileprivKey <- case headMay privKeys ofJust (PrivKeyRSA k) -> pure kJust (PrivKeyDSA _) -> fail "DSA keys not supported for payment request signing."Nothing -> fail $ "No keys found in private key file " <> encodeString (c ^. signingKeyFile)let pkiData = BT.X509SHA256 . CertificateChain $ pkiEntriespure $ PaymentsConfig (c ^. network) privKey pkiData - edit in lib/Aftok/Database/PostgreSQL.hs at line 3
{-# LANGUAGE QuasiQuotes #-} - replacement in lib/Aftok/Database/PostgreSQL.hs at line 5
module Aftok.Database.PostgreSQL (QDBM(), runQDBM) wheremodule Aftok.Database.PostgreSQL (QDBM(..), runQDBM) where - replacement in lib/Aftok/Database/PostgreSQL.hs at line 9
import Control.Monad.Trans.Eitherimport Control.Monad.Trans.Except (ExceptT(..), throwE, runExceptT) - edit in lib/Aftok/Database/PostgreSQL.hs at line 25
import Database.PostgreSQL.Simple.SqlQQ (sql) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 44
newtype QDBM a = QDBM (ReaderT Connection (EitherT DBError IO) a)newtype QDBM a = QDBM (ReaderT Connection (ExceptT DBError IO) a) - edit in lib/Aftok/Database/PostgreSQL.hs at line 53
instance MonadDB QDBM whereliftdb = pgEval - replacement in lib/Aftok/Database/PostgreSQL.hs at line 56
runQDBM :: Connection -> QDBM a -> EitherT DBError IO arunQDBM :: Connection -> QDBM a -> ExceptT DBError IO a - replacement in lib/Aftok/Database/PostgreSQL.hs at line 94
parser "credit_to_btc_addr" = CreditToAddress <$> (fieldWith btcAddrParser <* nullField <* nullField)parser "credit_to_address" = CreditToAddress <$> (fieldWith btcAddrParser <* nullField <* nullField) - edit in lib/Aftok/Database/PostgreSQL.hs at line 163
<*> field<*> field - replacement in lib/Aftok/Database/PostgreSQL.hs at line 218
lift . EitherT $ withTransaction conn (runEitherT $ runReaderT rt conn)lift . ExceptT $ withTransaction conn (runExceptT $ runReaderT rt conn) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 240
pinsert EventId"INSERT INTO aftok_events \\(event_time, created_by, event_type, event_json) \\VALUES (?, ?, ?, ?) RETURNING id"pinsert EventId[sql| INSERT INTO aftok_events(event_time, created_by, event_type, event_json)VALUES (?, ?, ?, ?) RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 250
pinsert EventId"INSERT INTO work_events \\(project_id, user_id, credit_to_type, credit_to_btc_addr, event_type, event_time, event_metadata) \\VALUES (?, ?, ?, ?, ?, ?, ?) \\RETURNING id"pinsert EventId[sql| INSERT INTO work_events( project_id, user_id, credit_to_type, credit_to_address, event_type, event_time, event_metadata )VALUES (?, ?, ?, ?, ?, ?, ?)RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 259
pinsert EventId"INSERT INTO work_events \\(project_id, user_id, credit_to_type, credit_to_project_id, event_type, event_time, event_metadata) \\VALUES (?, ?, ?, ?, ?, ?, ?) \\RETURNING id"pinsert EventId[sql| INSERT INTO work_events( project_id, user_id, credit_to_type, credit_to_project_id, event_type, event_time, event_metadata )VALUES (?, ?, ?, ?, ?, ?, ?)RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 268
pinsert EventId"INSERT INTO work_events \\(project_id, user_id, credit_to_type, credit_to_user_id, event_type, event_time, event_metadata) \\VALUES (?, ?, ?, ?, ?, ?, ?) \\RETURNING id"pinsert EventId[sql| INSERT INTO work_events(project_id, user_id, credit_to_type, credit_to_user_id, event_type, event_time, event_metadata)VALUES (?, ?, ?, ?, ?, ?, ?)RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 276
headMay <$> pquery qdbLogEntryParser"SELECT project_id, user_id, \\credit_to_type, credit_to_btc_addr, credit_to_user_id, credit_to_project_id, \\event_type, event_time, event_metadata FROM work_events \\WHERE id = ?"headMay <$> pquery qdbLogEntryParser[sql| SELECT project_id, user_id,credit_to_type, credit_to_address, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadata FROM work_eventsWHERE id = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 284
let q (Before e) = pquery logEntryParser"SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \\WHERE project_id = ? AND user_id = ? AND event_time <= ?"let q (Before e) = pquery logEntryParser[sql| SELECT credit_to_type, credit_to_address, credit_to_user_id, credit_to_project_id,event_type, event_time,event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ? AND event_time <= ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 291
q (During s e) = pquery logEntryParser"SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \\WHERE project_id = ? AND user_id = ? \\AND event_time >= ? AND event_time <= ?"q (During s e) = pquery logEntryParser[sql| SELECT credit_to_type, credit_to_address, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ?AND event_time >= ? AND event_time <= ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 298
q (After s) = pquery logEntryParser"SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \\WHERE project_id = ? AND user_id = ? AND event_time >= ?"q (After s) = pquery logEntryParser[sql| SELECT credit_to_type, credit_to_address, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ? AND event_time >= ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 307
pinsert AmendmentId"INSERT INTO event_time_amendments \\(event_id, amended_at, event_time) \\VALUES (?, ?, ?) RETURNING id"pinsert AmendmentId[sql| INSERT INTO event_time_amendments(event_id, amended_at, event_time)VALUES (?, ?, ?) RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 316
pinsert AmendmentId"INSERT INTO event_credit_to_amendments \\(event_id, amended_at, credit_to_type, credit_to_btc_addr) \\VALUES (?, ?, ?, ?) RETURNING id"pinsert AmendmentId[sql| INSERT INTO event_credit_to_amendments(event_id, amended_at, credit_to_type, credit_to_btc_addr)VALUES (?, ?, ?, ?) RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 323
pinsert AmendmentId"INSERT INTO event_credit_to_amendments \\(event_id, amended_at, credit_to_type, credit_to_project_id) \\VALUES (?, ?, ?, ?) RETURNING id"pinsert AmendmentId[sql| INSERT INTO event_credit_to_amendments(event_id, amended_at, credit_to_type, credit_to_project_id)VALUES (?, ?, ?, ?) RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 330
pinsert AmendmentId"INSERT INTO event_credit_to_amendments \\(event_id, amended_at, credit_to_type, credit_to_user_id) \\VALUES (?, ?, ?, ?) RETURNING id"pinsert AmendmentId[sql| INSERT INTO event_credit_to_amendments(event_id, amended_at, credit_to_type, credit_to_user_id)VALUES (?, ?, ?, ?) RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 337
pinsert AmendmentId"INSERT INTO event_metadata_amendments \\(event_id, amended_at, event_metadata) \\VALUES (?, ?, ?) RETURNING id"pinsert AmendmentId[sql| INSERT INTO event_metadata_amendments(event_id, amended_at, event_metadata)VALUES (?, ?, ?) RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 344
logEntries <- pquery logEntryParser"SELECT btc_addr, event_type, event_time, event_metadata FROM work_events WHERE project_id = ?"logEntries <- pquery logEntryParser[sql| SELECT credit_to_type, credit_to_address, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 353
pinsert A.AuctionId"INSERT INTO auctions (project_id, user_id, raise_amount, end_time) \\VALUES (?, ?, ?, ?) RETURNING id"pinsert A.AuctionId[sql| INSERT INTO auctions (project_id, initiator_id, raise_amount, end_time)VALUES (?, ?, ?, ?) RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 363
headMay <$> pquery auctionParser"SELECT project_id, initiator_id, created_at, raise_amount, start_time, end_time FROM auctions WHERE id = ?"headMay <$> pquery auctionParser[sql| SELECT project_id, initiator_id, created_at, raise_amount, start_time, end_timeFROM auctionsWHERE id = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 370
pinsert A.BidId"INSERT INTO bids (auction_id, bidder_id, bid_seconds, bid_amount, bid_time) \\VALUES (?, ?, ?, ?, ?) RETURNING id"pinsert A.BidId[sql| INSERT INTO bids (auction_id, bidder_id, bid_seconds, bid_amount, bid_time)VALUES (?, ?, ?, ?, ?) RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 381
pquery ((,) <$> idParser A.BidId <*> bidParser)"SELECT id, user_id, bid_seconds, bid_amount, bid_time FROM bids WHERE auction_id = ?"pquery ((,) <$> idParser A.BidId <*> bidParser)[sql| SELECT id, bidder_id, bid_seconds, bid_amount, bid_time FROM bids WHERE auction_id = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 388
in pinsert UserId"INSERT INTO users (handle, btc_addr, email) VALUES (?, ?, ?) RETURNING id"in pinsert UserId[sql| INSERT INTO users (handle, btc_addr, email) VALUES (?, ?, ?) RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 396
headMay <$> pquery userParser"SELECT handle, btc_addr, email FROM users WHERE id = ?"headMay <$> pquery userParser[sql| SELECT handle, btc_addr, email FROM users WHERE id = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 401
headMay <$> pquery ((,) <$> idParser UserId <*> userParser)"SELECT id, handle, btc_addr, email FROM users WHERE handle = ?"headMay <$> pquery ((,) <$> idParser UserId <*> userParser)[sql| SELECT id, handle, btc_addr, email FROM users WHERE handle = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 407
void $ pexec"INSERT INTO invitations (project_id, invitor_id, invitee_email, invitation_key, invitation_time) \\VALUES (?, ?, ?, ?, ?)"void $ pexec[sql| INSERT INTO invitations (project_id, invitor_id, invitee_email, invitation_key, invitation_time)VALUES (?, ?, ?, ?, ?) |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 414
headMay <$> pquery invitationParser"SELECT project_id, invitor_id, invitee_email, invitation_time, acceptance_time \\FROM invitations WHERE invitation_key = ?"headMay <$> pquery invitationParser[sql| SELECT project_id, invitor_id, invitee_email, invitation_time, acceptance_timeFROM invitations WHERE invitation_key = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 420
void $ pexec"UPDATE invitations SET acceptance_time = ? WHERE invitation_key = ?"void $ pexec[sql| UPDATE invitations SET acceptance_time = ? WHERE invitation_key = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 423
void $ pexec"INSERT INTO project_companions (project_id, user_id, invited_by, joined_at) \\SELECT i.project_id, ?, i.invitor_id, ? \\FROM invitations i \\WHERE i.invitation_key = ?"void $ pexec[sql| INSERT INTO project_companions (project_id, user_id, invited_by, joined_at)SELECT i.project_id, ?, i.invitor_id, ?FROM invitations iWHERE i.invitation_key = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 432
pinsert P.ProjectId"INSERT INTO projects (project_name, inception_date, initiator_id, depreciation_fn) \\VALUES (?, ?, ?, ?) RETURNING id"pinsert P.ProjectId[sql| INSERT INTO projects (project_name, inception_date, initiator_id, depreciation_fn)VALUES (?, ?, ?, ?) RETURNING id |] - edit in lib/Aftok/Database/PostgreSQL.hs at line 436
pgEval ListProjects =pquery (idParser P.ProjectId)[sql| SELECT id FROM projects |]()pgEval (FindSubscribers pid) =pquery (idParser UserId)[sql| SELECT s.user_idFROM subscripions sJOIN billables b ON s.billable_id = b.idWHERE b.project_id = ? |](Only (pid ^. P._ProjectId)) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 451
headMay <$> pquery projectParser"SELECT project_name, inception_date, initiator_id, depreciation_fn FROM projects WHERE id = ?"headMay <$> pquery projectParser[sql| SELECT project_name, inception_date, initiator_id, depreciation_fn FROM projects WHERE id = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 456
pquery ((,) <$> idParser P.ProjectId <*> projectParser)"SELECT p.id, p.project_name, p.inception_date, p.initiator_id, p.depreciation_fn \\FROM projects p LEFT OUTER JOIN project_companions pc ON pc.project_id = p.id \\WHERE pc.user_id = ? \\OR p.initiator_id = ?"pquery ((,) <$> idParser P.ProjectId <*> projectParser)[sql| SELECT p.id, p.project_name, p.inception_date, p.initiator_id, p.depreciation_fnFROM projects p LEFT OUTER JOIN project_companions pc ON pc.project_id = p.idWHERE pc.user_id = ?OR p.initiator_id = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 464
pexec"INSERT INTO project_companions (project_id, user_id, invited_by) VALUES (?, ?, ?)"pexec[sql| INSERT INTO project_companions (project_id, user_id, invited_by) VALUES (?, ?, ?) |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 470[4.11949]→[4.5294:5317](∅→∅),[4.5317]→[4.11973:12002](∅→∅),[4.11973]→[4.11973:12002](∅→∅),[4.12002]→[4.1090:1226](∅→∅),[4.522]→[4.12120:12171](∅→∅),[4.1226]→[4.12120:12171](∅→∅),[4.12120]→[4.12120:12171](∅→∅)
pinsert B.BillableId"INSERT INTO billables \\( project_id, event_id, name, description \\, recurrence_type, recurrence_count \\, billing_amount, grace_period_days) \\VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING id"pinsert B.BillableId[sql| INSERT INTO billables( project_id, event_id, name, description, recurrence_type, recurrence_count, billing_amount, grace_period_days, payment_request_email_template, payment_request_memo_template)VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id |] - edit in lib/Aftok/Database/PostgreSQL.hs at line 486
, b ^. (B.paymentRequestEmailTemplate), b ^. (B.paymentRequestMemoTemplate) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 491
headMay <$> pquery billableParser"SELECT b.project_id, e.created_by, b.name, b.description, b.recurrence_type, b.recurrence_count, \\ b.billing_amount, b.grace_period_days \\FROM billables b JOIN aftok_events e ON e.id = b.event_id \\WHERE b.id = ?"headMay <$> pquery billableParser[sql| SELECT b.project_id, e.created_by, b.name, b.description,b.recurrence_type, b.recurrence_count,b.billing_amount, b.grace_period_days,b.payment_request_email_template, b.payment_request_memo_templateFROM billables b JOIN aftok_events e ON e.id = b.event_idWHERE b.id = ? |] - edit in lib/Aftok/Database/PostgreSQL.hs at line 499
pgEval (FindBillables pid) =pquery ((,) <$> idParser B.BillableId <*> billableParser)[sql| SELECT b.id, b.project_id, e.created_by, b.name, b.description,b.recurrence_type, b.recurrence_count,b.billing_amount, b.grace_period_daysb.payment_request_email_template, b.payment_request_memo_templateFROM billables b JOIN aftok_events e ON e.id = b.event_idWHERE b.project_id = ? |](Only (pid ^. P._ProjectId)) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 512[4.609]→[4.5676:5703](∅→∅),[4.5703]→[4.637:710](∅→∅),[4.637]→[4.637:710](∅→∅),[4.710]→[4.1287:1326](∅→∅)
pinsert B.SubscriptionId"INSERT INTO subscriptions \\(user_id, billable_id, event_id) \\VALUES (?, ?, ?, ?) RETURNING id"pinsert B.SubscriptionId[sql| INSERT INTO subscriptions(user_id, billable_id, event_id, start_date)VALUES (?, ?, ?, ?) RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 523
headMay <$> pquery subscriptionParser"SELECT id, billable_id, start_date, end_date \\FROM subscriptions s \\WHERE s.id = ?"headMay <$> pquery subscriptionParser[sql| SELECT id, billable_id, start_date, end_dateFROM subscriptions sWHERE s.id = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 530[4.1554]→[4.3288:3356](∅→∅),[4.5820]→[4.3288:3356](∅→∅),[4.3356]→[4.654:715](∅→∅),[4.715]→[4.5935:6065](∅→∅),[4.5935]→[4.5935:6065](∅→∅)
pquery ((,) <$> idParser B.SubscriptionId <*> subscriptionParser)"SELECT id, user_id, billable_id, start_date, end_date \\FROM subscriptions s \\JOIN billables b ON b.id = s.billable_id \\WHERE s.user_id = ? \\AND b.project_id = ?"pquery ((,) <$> idParser B.SubscriptionId <*> subscriptionParser)[sql| SELECT s.id, user_id, billable_id, start_date, end_dateFROM subscriptions sJOIN billables b ON b.id = s.billable_idWHERE s.user_id = ?AND b.project_id = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 541
pinsert PaymentRequestId"INSERT INTO payment_requests \\(subscription_id, event_id, request_data, url_key, request_time, billing_date) \\VALUES (?, ?, ?, ?, ?, ?) RETURNING id"pinsert PaymentRequestId[sql| INSERT INTO payment_requests(subscription_id, event_id, request_data, url_key, request_time, billing_date)VALUES (?, ?, ?, ?, ?, ?) RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 554[4.730]→[4.1130:1295](∅→∅),[4.1295]→[4.3716:3743](∅→∅),[4.3716]→[4.3716:3743](∅→∅),[4.3743]→[4.1296:1379](∅→∅)
headMay <$> pquery ((,) <$> idParser PaymentRequestId <*> paymentRequestParser)"SELECT id, subscription_id, request_data, url_key, request_time, billing_date \\FROM payment_requests \\WHERE url_key = ? \\AND id NOT IN (SELECT payment_request_id FROM payments)"headMay <$> pquery ((,) <$> idParser PaymentRequestId <*> paymentRequestParser)[sql| SELECT id, subscription_id, request_data, url_key, request_time, billing_dateFROM payment_requestsWHERE url_key = ?AND id NOT IN (SELECT payment_request_id FROM payments) |] - edit in lib/Aftok/Database/PostgreSQL.hs at line 560
pgEval (FindPaymentRequestId (PaymentRequestId prid)) =headMay <$> pquery paymentRequestParser[sql| SELECT subscription_id, request_data, url_key, request_time, billing_dateFROM payment_requestsWHERE id = ? |](Only prid) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 569[4.1626]→[4.3835:3905](∅→∅),[4.3835]→[4.3835:3905](∅→∅),[4.3905]→[4.1380:1463](∅→∅),[4.1463]→[4.3979:4036](∅→∅),[4.3979]→[4.3979:4036](∅→∅)
pquery ((,) <$> idParser PaymentRequestId <*> paymentRequestParser)"SELECT id, subscription_id, request_data, url_key, request_time, billing_date \\FROM payment_requests \\WHERE subscription_id = ?"pquery ((,) <$> idParser PaymentRequestId <*> paymentRequestParser)[sql| SELECT id, subscription_id, request_data, url_key, request_time, billing_dateFROM payment_requestsWHERE subscription_id = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 581[4.1010]→[4.1010:1028](∅→∅),[4.1028]→[4.893:920](∅→∅),[4.920]→[4.1464:1558](∅→∅),[4.1558]→[4.1131:1601](∅→∅),[4.1131]→[4.1131:1601](∅→∅)
in pquery rowp"SELECT r.url_key, \\ r.subscription_id, r.request_data, r.url_key, r.request_time, r.billing_date, \\ s.user_id, s.billable_id, s.start_date, s.end_date, \\ b.project_id, e.created_by, b.name, b.description, b.recurrence_type, \\ b.recurrence_count, b.billing_amount, b.grace_period_days \\FROM payment_requests r \\JOIN subscriptions s on s.id = r.subscription_id \\JOIN billables b on b.id = s.billable_id \\WHERE subscription_id = ? \\AND r.id NOT IN (SELECT payment_request_id FROM payments)"in pquery rowp[sql| SELECT r.url_key,r.subscription_id, r.request_data, r.url_key, r.request_time, r.billing_date,s.user_id, s.billable_id, s.start_date, s.end_date,b.project_id, e.created_by, b.name, b.description, b.recurrence_type,b.recurrence_count, b.billing_amount, b.grace_period_days,b.payment_request_email_template, b.payment_request_memo_templateFROM payment_requests rJOIN subscriptions s on s.id = r.subscription_idJOIN billables b on b.id = s.billable_idJOIN aftok_events e on e.id = b.event_idWHERE subscription_id = ?AND r.id NOT IN (SELECT payment_request_id FROM payments) |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 598[4.81]→[4.81:129](∅→∅),[4.129]→[4.286:368](∅→∅),[4.368]→[4.4179:4218](∅→∅),[4.4179]→[4.4179:4218](∅→∅)
pinsert PaymentId"INSERT INTO payments \\(payment_request_id, event_id, payment_data, payment_date, exchange_rates) \\VALUES (?, ?, ?, ?) RETURNING id"pinsert PaymentId[sql| INSERT INTO payments(payment_request_id, event_id, payment_data, payment_date, exchange_rates)VALUES (?, ?, ?, ?, ?) RETURNING id |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 610
pquery ((,) <$> idParser PaymentId <*> paymentParser)"SELECT id, payment_request_id, payment_data, payment_date \\FROM payments \\WHERE payment_request_id = ?"pquery ((,) <$> idParser PaymentId <*> paymentParser)[sql| SELECT id, payment_request_id, payment_data, payment_dateFROM paymentsWHERE payment_request_id = ? |] - replacement in lib/Aftok/Database/PostgreSQL.hs at line 622[4.13185]→[4.13185:13217](∅→∅),[4.13217]→[4.3946:3947](∅→∅),[4.3946]→[4.3946:3947](∅→∅),[4.3947]→[4.6295:6341](∅→∅)
raiseError = QDBM . lift . leftinstance MonadDB QDBM whereliftdb = pgEval[4.13185]raiseError = QDBM . lift . throwE - edit in lib/Aftok/Database.hs at line 6
{-# LANGUAGE TemplateHaskell #-} - replacement in lib/Aftok/Database.hs at line 11
import Control.Lens (view, (^.))import Control.Lens (view, (^.), makeClassyPrisms, traverseOf) - edit in lib/Aftok/Database.hs at line 37
ListProjects :: DBOp [ProjectId]FindSubscribers :: ProjectId -> DBOp [UserId] - edit in lib/Aftok/Database.hs at line 58
FindBillables :: ProjectId -> DBOp [(BillableId, Billable)] - edit in lib/Aftok/Database.hs at line 68
FindPaymentRequestId :: PaymentRequestId -> DBOp (Maybe PaymentRequest) - edit in lib/Aftok/Database.hs at line 87
makeClassyPrisms ''DBError - replacement in lib/Aftok/Database.hs at line 108
findUser :: (MonadDB m) => UserId -> m (Maybe User)findUser = liftdb . FindUserfindUser :: (MonadDB m) => UserId -> MaybeT m UserfindUser = MaybeT . liftdb . FindUser - replacement in lib/Aftok/Database.hs at line 111
findUserByName :: (MonadDB m) => UserName -> m (Maybe (UserId, User))findUserByName = liftdb . FindUserByNamefindUserByName :: (MonadDB m) => UserName -> MaybeT m (UserId, User)findUserByName = MaybeT . liftdb . FindUserByName - edit in lib/Aftok/Database.hs at line 121
listProjects :: (MonadDB m) => m [ProjectId]listProjects = liftdb ListProjectsfindSubscribers :: (MonadDB m) => ProjectId -> m [UserId]findSubscribers = liftdb . FindSubscribers - replacement in lib/Aftok/Database.hs at line 128[4.7810]→[4.7417:7488](∅→∅),[4.7488]→[4.7871:7896](∅→∅),[4.7871]→[4.7871:7896](∅→∅),[4.7896]→[4.533:563](∅→∅),[4.563]→[4.7927:7984](∅→∅),[4.7927]→[4.7927:7984](∅→∅)
findProject :: (MonadDB m) => ProjectId -> UserId -> m (Maybe Project)findProject pid uid = dokps <- findUserProjects uidpure $ fmap snd (find (\(pid', _) -> pid' == pid) kps)findProject :: (MonadDB m) => ProjectId -> MaybeT m ProjectfindProject = MaybeT . liftdb . FindProjectfindProjectOrError :: (MonadDB m) => ProjectId -> m ProjectfindProjectOrError pid = fromMaybeT(raiseSubjectNotFound $ FindProject pid)(findProject pid)findUserProject :: (MonadDB m) => UserId -> ProjectId -> MaybeT m ProjectfindUserProject uid pid = dokps <- lift $ findUserProjects uidMaybeT . pure $ fmap snd (find (\(pid', _) -> pid' == pid) kps) - replacement in lib/Aftok/Database.hs at line 221
traverse findBillable subtraverseOf B.billable findBillable sub - replacement in lib/Aftok/Database.hs at line 226
findPaymentRequest :: (MonadDB m) => PaymentKey -> m (Maybe (PaymentRequestId, PaymentRequest))findPaymentRequest = liftdb . FindPaymentRequestfindPaymentRequest :: (MonadDB m) => PaymentKey -> MaybeT m (PaymentRequestId, PaymentRequest)findPaymentRequest = MaybeT . liftdb . FindPaymentRequestfindPaymentRequestId :: (MonadDB m) => PaymentRequestId -> MaybeT m PaymentRequestfindPaymentRequestId = MaybeT . liftdb . FindPaymentRequestId - replacement in lib/Aftok/Database.hs at line 237
findPayment :: (MonadDB m) => PaymentRequestId -> m (Maybe Payment)findPayment prid = (fmap snd . headMay) <$> liftdb (FindPayments prid)findPayment :: (MonadDB m) => PaymentRequestId -> MaybeT m PaymentfindPayment prid = MaybeT $ (fmap snd . headMay) <$> liftdb (FindPayments prid) - replacement in lib/Aftok/Database.hs at line 246
findAuction :: (MonadDB m) => AuctionId -> UserId -> m (Maybe Auction)findAuction :: (MonadDB m) => AuctionId -> UserId -> MaybeT m Auction - replacement in lib/Aftok/Database.hs at line 250[4.892]→[4.9724:9754](∅→∅),[4.9754]→[4.205:291](∅→∅),[4.205]→[4.205:291](∅→∅),[4.291]→[4.1006:1024](∅→∅),[4.1006]→[4.1006:1024](∅→∅)
maybeAuc <- liftdb findOp_ <- traverse (\auc -> checkProjectAuth (auc ^. A.projectId) uid findOp) maybeAucpure maybeAucauc <- MaybeT $ liftdb findOp_ <- lift $ checkProjectAuth (auc ^. A.projectId) uid findOppure auc - replacement in lib/Aftok/Json.hs at line 111[4.486]→[4.6279:6327](∅→∅),[4.6327]→[4.6629:6666](∅→∅),[4.9375]→[4.6629:6666](∅→∅),[4.6666]→[4.1803:1850](∅→∅),[4.1850]→[4.1247:1288](∅→∅),[4.1247]→[4.1247:1288](∅→∅)
qdbProjectJSON :: (ProjectId, Project) -> ValueqdbProjectJSON (pid, project) = v1 $obj [ "projectId" .= idValue _ProjectId pid, "project" .= projectJSON projectqdbJSON :: Text -> (Lens' a UUID) -> (b -> Value) -> (a, b) -> ValueqdbJSON name l f (xid, x) = v1 $obj [ (name <> "Id") .= idValue l xid, name .= f x - edit in lib/Aftok/Json.hs at line 126
qdbProjectJSON :: (ProjectId, Project) -> ValueqdbProjectJSON = qdbJSON "project" _ProjectId projectJSON - edit in lib/Aftok/Json.hs at line 180
billableIdJSON :: B.BillableId -> ValuebillableIdJSON = idJSON "billableId" B._BillableId - edit in lib/Aftok/Json.hs at line 197
qdbBillableJSON :: (B.BillableId, B.Billable) -> ValueqdbBillableJSON = qdbJSON "billable" B._BillableId billableJSON - edit in lib/Aftok/Json.hs at line 226
subscriptionIdJSON :: B.SubscriptionId -> ValuesubscriptionIdJSON = idJSON "subscriptionId" B._SubscriptionId - edit in lib/Aftok/Json.hs at line 255
paymentIdJSON :: PaymentId -> ValuepaymentIdJSON = idJSON "paymentId" _PaymentId - edit in lib/Aftok/Json.hs at line 271
parseUUID :: Value -> Parser U.UUIDparseUUID v = dostr <- parseJSON vmaybe (fail $ "Value " <> str <> "Could not be parsed as a valid UUID.") pure $ U.fromString strparseId :: forall a. Prism' a UUID -> Value -> Parser aparseId p = fmap (review p) . parseUUID - replacement in lib/Aftok/Json.hs at line 294
fmap (CreditToUser . UserId) . parseUUID <$> O.lookup "creditToUser" o'fmap CreditToUser . parseId _UserId <$> O.lookup "creditToUser" o' - replacement in lib/Aftok/Json.hs at line 297
fmap (CreditToProject . ProjectId) . parseUUID <$> O.lookup "creditToProject" o'fmap CreditToProject . parseId _ProjectId <$> O.lookup "creditToProject" o' - edit in lib/Aftok/Json.hs at line 345
parseUUID :: Value -> Parser U.UUIDparseUUID v = dostr <- parseJSON vmaybe (fail $ "Value " <> str <> "Could not be parsed as a valid UUID.") pure $ U.fromString str - edit in lib/Aftok/Json.hs at line 354
parseBillable :: Value -> Parser B.BillableparseBillable = unversion "Billable" p where--p (Version 1 0) o = - replacement in lib/Aftok/Json.hs at line 355
p v o = badVersion "Billable" v oparseRecurrence :: Object -> Parser B.RecurrenceparseRecurrence o =let parseAnnually o' = const (pure B.Annually) <$> O.lookup "annually" o'parseMonthly o' = fmap B.Monthly . parseJSON <$> O.lookup "monthly" o'parseWeekly o' = fmap B.Weekly . parseJSON <$> O.lookup "weekly" o'parseOneTime o' = const (pure B.OneTime) <$> O.lookup "one-time" o' - edit in lib/Aftok/Json.hs at line 362
notFound = fail $ "Value " <> show o <> " does not represent a Recurrence value."parseV v = parseAnnually v <|> parseMonthly v <|> parseWeekly v <|> parseOneTime vin fromMaybe notFound $ parseV o - edit in lib/Aftok/Json.hs at line 366[4.3265]
parseRecurrence' :: Value -> Parser B.RecurrenceparseRecurrence' (Object o) = parseRecurrence oparseRecurrence' v = fail $ "Value " <> show v <> " is not a JSON object." - replacement in lib/Aftok/Payments/Types.hs at line 20
getPaymentDetails)getPaymentDetails, Satoshi(..)) - edit in lib/Aftok/Payments/Types.hs at line 31
-- A unique identifier for the payment request, suitable-- for URL embedding. - edit in lib/Aftok/Payments/Types.hs at line 71[4.1513]
paymentRequestTotal :: P.PaymentRequest -> SatoshipaymentRequestTotal _ = error "Not yet implemented" - replacement in lib/Aftok/Payments.hs at line 14
view, (%~), (^.))view, (%~), (^.), traverseOf) - edit in lib/Aftok/Payments.hs at line 17
import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT) - replacement in lib/Aftok/Payments.hs at line 42[4.12460]→[4.12460:12495](∅→∅),[4.12495]→[4.3815:3845](∅→∅),[4.3845]→[4.7697:7731](∅→∅),[4.12522]→[4.7697:7731](∅→∅),[4.7731]→[4.3846:3876](∅→∅)
data BillingConfig = BillingConfig{ _network :: BT.Network, _signingKey :: RSA.PrivateKey, _pkiData :: BT.PKIDatadata PaymentsConfig = PaymentsConfig{ _network :: !BT.Network, _signingKey :: !RSA.PrivateKey, _pkiData :: !BT.PKIData - replacement in lib/Aftok/Payments.hs at line 47
makeClassy ''BillingConfigmakeClassy ''PaymentsConfig - replacement in lib/Aftok/Payments.hs at line 51
memoGen :: Subscription' UserId Billable -> T.Day -> C.UTCTime -> m (Maybe Text)memoGen :: Subscription' UserId Billable -- ^ subscription being billed-> T.Day -- ^ billing date-> C.UTCTime -- ^ payment request generation time-> m (Maybe Text) - replacement in lib/Aftok/Payments.hs at line 56
, uriGen :: PaymentKey -> m (Maybe URI), uriGen :: PaymentKey -- ^ payment key to be included in the URL-> m (Maybe URI) - replacement in lib/Aftok/Payments.hs at line 59
, payloadGen :: Subscription' UserId Billable -> T.Day -> C.UTCTime -> m (Maybe ByteString), payloadGen :: Subscription' UserId Billable -- ^ subscription being billed-> T.Day -- ^ billing date-> C.UTCTime -- ^ payment request generation time-> m (Maybe ByteString) - replacement in lib/Aftok/Payments.hs at line 66
= Paid Payment -- ^ the request was paid with the specified payment| Unpaid PaymentRequest -- ^ the request has not been paid, but has not yet expired| Expired PaymentRequest -- ^ the request was not paid prior to the expiration date= Paid !Payment -- ^ the request was paid with the specified payment| Unpaid !PaymentRequest -- ^ the request has not been paid, but has not yet expired| Expired !PaymentRequest -- ^ the request was not paid prior to the expiration date - replacement in lib/Aftok/Payments.hs at line 71
= Overdue SubscriptionId| SigningError RSA.Error= Overdue !SubscriptionId| SigningError !RSA.Error - edit in lib/Aftok/Payments.hs at line 75
{--- Find all the subscriptions for the specified customer, and- determine which if any are up for renewal. Create a payment- request for each such subscription.--} - replacement in lib/Aftok/Payments.hs at line 81
, MonadReader r m, HasBillingConfig r, MonadReader r m, HasPaymentsConfig r - replacement in lib/Aftok/Payments.hs at line 96
, MonadReader r m, HasBillingConfig r, MonadReader r m, HasPaymentsConfig r - replacement in lib/Aftok/Payments.hs at line 106
traverse findBillable subtraverseOf billable findBillable sub - replacement in lib/Aftok/Payments.hs at line 114
, MonadReader r m, HasBillingConfig r, MonadReader r m, HasPaymentsConfig r - replacement in lib/Aftok/Payments.hs at line 167
in maybe ifUnpaid Paid <$> findPayment reqidin maybe ifUnpaid Paid <$> runMaybeT (findPayment reqid) - replacement in lib/Aftok/Payments.hs at line 171
createPaymentDetails :: (MonadRandom m, MonadReader r m, HasBillingConfig r, MonadDB m)createPaymentDetails :: (MonadRandom m, MonadReader r m, HasPaymentsConfig r, MonadDB m) - replacement in lib/Aftok/Payments.hs at line 215
createOutputs _ (TL.CreditToUser uid) amt = doaddrMay <- (>>= view userAddress) <$> findUser uidlet createOutput addr = BT.Output amt (PayPKHash (addr ^. _BtcAddr))pure . maybeToList $ createOutput <$> addrMaycreateOutputs _ (TL.CreditToUser uid) amt = (fmap maybeToList) . runMaybeT $ douser <- findUser uidaddr <- MaybeT . pure $ user ^. userAddresspure $ BT.Output amt (PayPKHash (addr ^. _BtcAddr)) - replacement in lib/Aftok/TimeLog.hs at line 38
data LogEvent = StartWork { _eventTime :: C.UTCTime }| StopWork { _eventTime :: C.UTCTime }data LogEvent = StartWork { _eventTime :: !C.UTCTime }| StopWork { _eventTime :: !C.UTCTime } - replacement in lib/Aftok/TimeLog.hs at line 60
= CreditToAddress BtcAddr= CreditToAddress !BtcAddr - replacement in lib/Aftok/TimeLog.hs at line 62
| CreditToUser UserId| CreditToUser !UserId - replacement in lib/Aftok/TimeLog.hs at line 64
| CreditToProject ProjectId| CreditToProject !ProjectId - replacement in lib/Aftok/TimeLog.hs at line 74[4.4721]→[4.4927:4954](∅→∅),[4.4954]→[4.4747:4774](∅→∅),[4.4747]→[4.4747:4774](∅→∅),[4.4774]→[4.1784:1816](∅→∅),[4.1784]→[4.1784:1816](∅→∅)
{ _creditTo :: CreditTo, _event :: LogEvent, _eventMeta :: Maybe A.Value{ _creditTo :: !CreditTo, _event :: !LogEvent, _eventMeta :: !(Maybe A.Value) - replacement in lib/Aftok/TimeLog.hs at line 91[4.5813]→[4.6721:6772](∅→∅),[4.6772]→[4.5005:5059](∅→∅),[4.5059]→[4.6824:6877](∅→∅),[4.6824]→[4.6824:6877](∅→∅)
data EventAmendment = TimeChange ModTime C.UTCTime| CreditToChange ModTime CreditTo| MetadataChange ModTime A.Valuedata EventAmendment = TimeChange !ModTime !C.UTCTime| CreditToChange !ModTime !CreditTo| MetadataChange !ModTime !A.Value - edit in lib/Aftok/Util.hs at line 7
import Control.Error.Util (maybeT) - edit in lib/Aftok/Util.hs at line 10
import Control.Monad.Trans.Maybe (MaybeT) - edit in lib/Aftok/Util.hs at line 32[4.7007]
fromMaybeT :: (Monad m) => m a -> MaybeT m a -> m afromMaybeT a m = maybeT a pure m - replacement in lib/Aftok.hs at line 44[4.2885]→[4.6188:6217](∅→∅),[4.6217]→[4.7359:7393](∅→∅),[4.7393]→[4.6218:6244](∅→∅),[4.2939]→[4.6218:6244](∅→∅)
{ _username :: UserName, _userAddress :: Maybe BtcAddr, _userEmail :: Email{ _username :: !UserName, _userAddress :: !(Maybe BtcAddr), _userEmail :: !Email - replacement in migrations/2016-10-14_02-49-36_event-amendments.txt at line 42
alter table work_events rename column credit_to_btc_addr to btc_addr;alter table work_events rename column credit_to_address to btc_addr; - file addition: 2017-06-08_04-37-31_event-metadata-ids.txt[86.1]
Description: Add missing identifiers to event metadata tablesCreated: 2017-06-08 04:38:05.341636 UTCDepends: 2016-10-14_02-49-36_event-amendmentsApply: |alter table event_metadata_amendmentsadd column id uuid primary key default uuid_generate_v4();alter table event_credit_to_amendmentsadd column id uuid primary key default uuid_generate_v4();alter table event_time_amendmentsadd column id uuid primary key default uuid_generate_v4(); - file addition: 2017-09-24_22-06-01_billing-templates.txt[86.1]
Description: (Describe migration here.)Created: 2017-09-24 22:06:53.509947 UTCDepends: 2016-12-31_03-45-17_create-paymentsApply: |alter table billables add column payment_request_email_template text null;alter table billables add column payment_request_memo_template text null;Revert: |alter table billables drop column payment_request_email_template;alter table billables drop column payment_request_memo_template; - replacement in server/Aftok/QConfig.hs at line 4
import ClassyPreludeimport ClassyPrelude hiding (FilePath) - replacement in server/Aftok/QConfig.hs at line 6
import qualified Data.ByteString.Char8 as Cimport qualified Data.ByteString.Char8 as C8 - replacement in server/Aftok/QConfig.hs at line 9[4.6698]→[4.33:60](∅→∅),[4.60]→[4.5065:5144](∅→∅),[4.5144]→[4.124:177](∅→∅),[4.124]→[4.124:177](∅→∅),[4.177]→[4.6698:6900](∅→∅),[4.16760]→[4.6698:6900](∅→∅),[4.6698]→[4.6698:6900](∅→∅)
import Data.X509import Data.X509.File (readKeyFile, readSignedObject)import qualified Network.Bippy.Types as BTimport qualified Network.Mail.SMTP as SMTPimport qualified Network.Socket as NSimport System.Environmentimport System.IO (FilePath)import System.Environment (getEnvironment)import Filesystem.Path.CurrentOS (FilePath, fromText, encodeString) - replacement in server/Aftok/QConfig.hs at line 16
import qualified Aftok.Payments as APimport Aftok.Config - replacement in server/Aftok/QConfig.hs at line 21
, authSiteKey :: System.IO.FilePath, authSiteKey :: FilePath - replacement in server/Aftok/QConfig.hs at line 26[4.253]→[4.7190:7230](∅→∅),[4.16792]→[4.7190:7230](∅→∅),[4.7190]→[4.7190:7230](∅→∅),[4.7230]→[4.2054:2096](∅→∅),[4.2096]→[4.7230:7234](∅→∅),[4.7230]→[4.7230:7234](∅→∅),[4.7234]→[4.377:378](∅→∅),[4.5084]→[4.377:378](∅→∅),[4.378]→[4.7235:7264](∅→∅),[4.7264]→[4.408:472](∅→∅),[4.408]→[4.408:472](∅→∅),[4.472]→[4.7265:7325](∅→∅)
, templatePath :: System.IO.FilePath, staticAssetPath :: System.IO.FilePath}data SmtpConfig = SmtpConfig{ smtpHost :: NS.HostName, smtpPort :: Maybe NS.PortNumber, smtpUser :: SMTP.UserName, smtpPass :: SMTP.Password, templatePath :: FilePath, staticAssetPath :: FilePath - replacement in server/Aftok/QConfig.hs at line 30[4.5085]→[4.254:289](∅→∅),[4.289]→[4.1020:1196](∅→∅),[4.1196]→[4.392:396](∅→∅),[4.5274]→[4.392:396](∅→∅),[4.392]→[4.392:396](∅→∅),[4.396]→[4.16851:16852](∅→∅),[4.16851]→[4.16851:16852](∅→∅),[4.16852]→[4.5085:5133](∅→∅),[4.5085]→[4.5085:5133](∅→∅)
data BillingConfig = BillingConfig{ network :: BT.Network, signingKeyFile :: System.IO.FilePath, certsFile :: System.IO.FilePath, exchangeRateServiceURI :: String}loadQConfig :: System.IO.FilePath -> IO QConfigloadQConfig :: FilePath -> IO QConfig - replacement in server/Aftok/QConfig.hs at line 33
cfg <- C.load [C.Required cfgFile]let dbEnvCfg = pgsDefaultConfig . C.pack <$> lookup "DATABASE_URL" envcfg <- C.load [C.Required $ encodeString cfgFile]let dbEnvCfg = pgsDefaultConfig . C8.pack <$> lookup "DATABASE_URL" env - replacement in server/Aftok/QConfig.hs at line 41
<*> C.require cfg "siteKey"<*> (fromText <$> C.require cfg "siteKey") - replacement in server/Aftok/QConfig.hs at line 46[2.266]→[4.3:83](∅→∅),[4.433]→[4.3:83](∅→∅),[4.572]→[4.3:83](∅→∅),[4.16885]→[4.3:83](∅→∅),[4.7925]→[4.3:83](∅→∅),[4.83]→[4.2097:2177](∅→∅)
<*> C.lookupDefault "/opt/aftok/server/templates/" cfg "templatePath"<*> C.lookupDefault "/opt/aftok/server/static/" cfg "staticAssetPath"<*> (fromText <$> C.lookupDefault "/opt/aftok/server/templates/" cfg "templatePath")<*> (fromText <$> C.lookupDefault "/opt/aftok/server/static/" cfg "staticAssetPath") - edit in server/Aftok/QConfig.hs at line 49[4.7969]→[4.573:618](∅→∅),[4.618]→[4.7503:7524](∅→∅),[4.7524]→[4.640:836](∅→∅),[4.640]→[4.640:836](∅→∅),[4.836]→[4.5811:5812](∅→∅),[4.8170]→[4.5811:5812](∅→∅),[4.5811]→[4.5811:5812](∅→∅),[4.5812]→[4.434:485](∅→∅),[4.485]→[4.5275:5299](∅→∅),[4.5299]→[4.510:670](∅→∅),[4.510]→[4.510:670](∅→∅),[4.670]→[4.1197:1256](∅→∅),[4.1256]→[4.670:756](∅→∅),[4.670]→[4.670:756](∅→∅),[4.756]→[4.5300:5342](∅→∅),[4.813]→[4.17124:17125](∅→∅),[4.5342]→[4.17124:17125](∅→∅),[4.17124]→[4.17124:17125](∅→∅)
readSmtpConfig :: CT.Config -> IO SmtpConfigreadSmtpConfig cfg =SmtpConfig <$> C.require cfg "smtpHost"<*> ((fmap . fmap) fromInteger $ C.lookup cfg "smtpPort")<*> C.require cfg "smtpUser"<*> C.require cfg "smtpKey"readBillingConfig :: CT.Config -> IO BillingConfigreadBillingConfig cfg =BillingConfig <$> (parseNetwork <$> C.require cfg "network")<*> C.require cfg "signingKeyFile"<*> C.require cfg "certsFile"<*> C.require cfg "exchangeRateServiceURI"where parseNetwork :: String -> BT.NetworkparseNetwork "main" = BT.MainNetparseNetwork _ = BT.TestNet - edit in server/Aftok/QConfig.hs at line 59
toBillingConfig :: BillingConfig -> IO AP.BillingConfigtoBillingConfig c = doprivKeys <- readKeyFile (signingKeyFile c)pkiEntries <- readSignedObject (certsFile c)privKey <- case headMay privKeys ofJust (PrivKeyRSA k) -> pure kJust (PrivKeyDSA _) -> fail "DSA keys not supported for payment request signing."Nothing -> fail $ "No keys found in private key file " <> signingKeyFile clet pkiData = BT.X509SHA256 . CertificateChain $ pkiEntriespure $ AP.BillingConfig (network c) privKey pkiData - edit in server/Aftok/Snaplet/Auctions.hs at line 10
import Control.Monad.Trans.Maybe (mapMaybeT) - edit in server/Aftok/Snaplet/Auctions.hs at line 25
import Aftok.Util (fromMaybeT) - replacement in server/Aftok/Snaplet/Auctions.hs at line 58
maybeAuc <- snapEval $ findAuction aid uid -- this will verify auction accessmaybe (snapError 404 $ "Auction not found for id " <> tshow aid) pure maybeAucfromMaybeT(snapError 404 $ "Auction not found for id " <> tshow aid)(mapMaybeT snapEval $ findAuction aid uid) -- this will verify auction access - edit in server/Aftok/Snaplet/Auth.hs at line 6
import Control.Error.Util (maybeT)import Control.Monad.Trans.Maybe (mapMaybeT) - replacement in server/Aftok/Snaplet/Auth.hs at line 34[4.1780]→[4.1400:1458](∅→∅),[4.1458]→[4.11752:11803](∅→∅),[4.1524]→[4.2024:2042](∅→∅),[4.11803]→[4.2024:2042](∅→∅),[4.2024]→[4.2024:2042](∅→∅),[4.2042]→[4.8265:8350](∅→∅),[4.8350]→[4.8071:8100](∅→∅),[4.2128]→[4.8071:8100](∅→∅)
currentUser <- UserName . AU.userLogin <$> requireLoginqdbUser <- snapEval $ findUserByName currentUsercase qdbUser ofNothing -> snapError 403 "Unable to retrieve user record for authenticated user"Just u -> pure (u ^. _1)currentUser <- UserName . AU.userLogin <$> requireUsermaybeT(snapError 403 "Unable to retrieve user record for authenticated user")(pure . (^. _1))(mapMaybeT snapEval $ findUserByName currentUser) - file addition: Billing.hs[4.2082]
{-# LANGUAGE TemplateHaskell #-}module Aftok.Snaplet.Billing( billableCreateHandler, billableListHandler, subscribeHandler) whereimport ClassyPreludeimport Control.Lens ((^.))import Data.Aesonimport Data.Aeson.Typesimport Data.Thyme.Clock as Cimport Data.Thyme.Time.Core (toThyme)import Snap.Snaplet as Simport Aftok (UserId)import Aftok.Billablesimport Aftok.Jsonimport Aftok.Typesimport Aftok.Projectimport Aftok.Database (createBillable, withProjectAuth, liftdb, DBOp(..))import Aftok.Snapletimport Aftok.Snaplet.AuthparseCreateBillable :: UserId -> ProjectId -> Value -> Parser BillableparseCreateBillable uid pid = unversion "Billable" p wherep (Version 1 0) o =Billable <$> pure pid<*> pure uid<*> o .: "name"<*> o .: "description"<*> (parseRecurrence' =<< o .: "recurrence")<*> (Satoshi <$> o .: "amount")<*> o .: "gracePeriod"<*> (fmap toThyme <$> o .: "requestExpiryPeriod")<*> o .:? "paymentRequestEmailTemplate"<*> o .:? "paymentRequestMemoTemplate"p v o = badVersion "Billable" v obillableCreateHandler :: S.Handler App App BillableIdbillableCreateHandler = douid <- requireUserIdpid <- requireProjectIdrequestBody <- readRequestJSON 4096b <- either (snapError 400 . tshow) pure $ parseEither (parseCreateBillable uid pid) requestBodysnapEval $ createBillable uid bbillableListHandler :: S.Handler App App [(BillableId, Billable)]billableListHandler = douid <- requireUserIdpid <- requireProjectIdsnapEval $ withProjectAuth pid uid (FindBillables pid)subscribeHandler :: S.Handler App App SubscriptionIdsubscribeHandler = douid <- requireUserIdbid <- requireId "billableId" BillableIdt <- liftIO C.getCurrentTimesnapEval . liftdb $ CreateSubscription uid bid (t ^. C._utctDay) - replacement in server/Aftok/Snaplet/Payments.hs at line 9
import ClassyPreludeimport ClassyPrelude - replacement in server/Aftok/Snaplet/Payments.hs at line 11
import Control.Lens (view, _1, _2, _Right, _Left, preview, (&), (.~))import Control.Lens (view, _1, _2, _Right, _Left, preview, (&), (.~), (^.))import Control.Monad.Trans.Maybe (mapMaybeT) - edit in server/Aftok/Snaplet/Payments.hs at line 26
import Aftok.Config as AC - replacement in server/Aftok/Snaplet/Payments.hs at line 29
import Aftok.Paymentsimport Aftok.Paymentsimport Aftok.Util (fromMaybeT) - edit in server/Aftok/Snaplet/Payments.hs at line 32
import Aftok.QConfig as QC - replacement in server/Aftok/Snaplet/Payments.hs at line 46
paymentResponseHandler :: QC.BillingConfig -> S.Handler App App PaymentIdpaymentResponseHandler :: AC.BillingConfig -> S.Handler App App PaymentId - replacement in server/Aftok/Snaplet/Payments.hs at line 59
exchResp <- liftIO . try $ asValue =<< (withOpenSSL $ getWith opts (exchangeRateServiceURI cfg))exchResp <- liftIO . try $ asValue =<< (withOpenSSL $ getWith opts (cfg ^. exchangeRateServiceURI)) - replacement in server/Aftok/Snaplet/Payments.hs at line 70
prMay <- snapEval $ findPaymentRequest pkeymaybe (snapError 404 $ "Outstanding payment request not found for key " <> (view _PaymentKey pkey))pure prMayfromMaybeT(snapError 404 $ "Outstanding payment request not found for key " <> (view _PaymentKey pkey))(mapMaybeT snapEval $ findPaymentRequest pkey) - replacement in server/Aftok/Snaplet/Projects.hs at line 11
import ClassyPreludeimport ClassyPrelude hiding (FilePath) - edit in server/Aftok/Snaplet/Projects.hs at line 14
import Control.Monad.Trans.Maybe (mapMaybeT, runMaybeT) - edit in server/Aftok/Snaplet/Projects.hs at line 18
import Filesystem.Path.CurrentOS (FilePath, encodeString) - edit in server/Aftok/Snaplet/Projects.hs at line 21
import System.IO (FilePath) - edit in server/Aftok/Snaplet/Projects.hs at line 24
import Aftok.Config - replacement in server/Aftok/Snaplet/Projects.hs at line 27
import Aftok.QConfigimport Aftok.QConfig as QC - edit in server/Aftok/Snaplet/Projects.hs at line 30
import Aftok.Util (fromMaybeT) - replacement in server/Aftok/Snaplet/Projects.hs at line 58
mp <- snapEval $ findProject pid uidmaybe (snapError 404 $ "Project not found for id " <> tshow pid) pure mpfromMaybeT(snapError 404 $ "Project not found for id " <> tshow pid)(mapMaybeT snapEval $ findUserProject uid pid) - replacement in server/Aftok/Snaplet/Projects.hs at line 69
(,,) <$> findUser uid<*> findProject pid uid(,,) <$> (runMaybeT $ findUser uid)<*> (runMaybeT $ findUserProject uid pid) - replacement in server/Aftok/Snaplet/Projects.hs at line 82
let SmtpConfig{..} = smtpConfig cfgmailer = maybe (sendMailWithLogin smtpHost) (sendMailWithLogin' smtpHost) smtpPortlet SmtpConfig{..} = QC.smtpConfig cfgmailer = maybe (sendMailWithLogin _smtpHost) (sendMailWithLogin' _smtpHost) _smtpPort - replacement in server/Aftok/Snaplet/Projects.hs at line 85
(mailer smtpUser smtpPass)(mailer _smtpUser _smtpPass) - replacement in server/Aftok/Snaplet/Projects.hs at line 88
buildProjectInviteEmail :: System.IO.FilePathbuildProjectInviteEmail :: FilePath - replacement in server/Aftok/Snaplet/Projects.hs at line 95
templates <- directoryGroup templatePathtemplates <- directoryGroup $ encodeString templatePath - edit in server/Aftok/Snaplet/WorkLog.hs at line 6
import Control.Monad.Trans.Maybe (mapMaybeT) - edit in server/Aftok/Snaplet/WorkLog.hs at line 19
import Aftok.Util (fromMaybeT) - replacement in server/Aftok/Snaplet/WorkLog.hs at line 74
projectMay <- snapEval $ findProject pid uidproject <- maybe (snapError 400 $ "Project not found for id " <> tshow pid) pure projectMayproject <- fromMaybeT(snapError 400 $ "Project not found for id " <> tshow pid)(mapMaybeT snapEval $ findUserProject uid pid) - replacement in server/Aftok/Snaplet.hs at line 11
import Control.Monad.Trans.Eitherimport Control.Monad.Trans.Except (runExceptT) - replacement in server/Aftok/Snaplet.hs at line 50
e <- liftPG $ \conn -> liftIO $ runEitherT (runQDBM conn $ interpret liftdb p)e <- liftPG $ \conn -> liftIO $ runExceptT (runQDBM conn $ interpret liftdb p) - replacement in server/Main.hs at line 3
import ClassyPreludeimport ClassyPrelude hiding (FilePath) - edit in server/Main.hs at line 6
import Data.Either.Combinators (fromRight) - edit in server/Main.hs at line 9
import Filesystem.Path.CurrentOS (decodeString, encodeString) - edit in server/Main.hs at line 18
import Aftok.Snaplet.Billing - replacement in server/Main.hs at line 35
cfg <- loadQConfig $ either (const "conf/aftok.cfg") id cfgPathcfg <- loadQConfig . decodeString $ fromRight "conf/aftok.cfg" cfgPath - replacement in server/Main.hs at line 42
initCookieSessionManager (authSiteKey cfg) "quookie" (Just "aftok.com") (cookieTimeout cfg)initCookieSessionManager (encodeString $ authSiteKey cfg) "quookie" (Just "aftok.com") (cookieTimeout cfg) - replacement in server/Main.hs at line 46
let loginRoute = method GET requireLogin >> redirect "/home"xhrLoginRoute = void $ method POST requireLoginregisterRoute = void $ method POST registerHandleracceptInviteRoute = void $ method POST acceptInvitationHandlerlet loginRoute = method GET requireLogin >> redirect "/home"xhrLoginRoute = void $ method POST requireLoginregisterRoute = void $ method POST registerHandlerinviteRoute = void $ method POST (projectInviteHandler cfg)acceptInviteRoute = void $ method POST acceptInvitationHandler - replacement in server/Main.hs at line 54
listProjectsRoute = serveJSON (fmap qdbProjectJSON) $ method GET projectListHandlerprojectListRoute = serveJSON (fmap qdbProjectJSON) $ method GET projectListHandler - edit in server/Main.hs at line 57
logWorkRoute f = serveJSON eventIdJSON $ method POST (logWorkHandler f)logWorkBTCRoute f = serveJSON eventIdJSON $ method POST (logWorkBTCHandler f) - edit in server/Main.hs at line 59
- replacement in server/Main.hs at line 61
inviteRoute = void . method POST $ projectInviteHandler cfglogWorkRoute f = serveJSON eventIdJSON $ method POST (logWorkHandler f)logWorkBTCRoute f = serveJSON eventIdJSON $ method POST (logWorkBTCHandler f)amendEventRoute = serveJSON amendmentIdJSON $ method PUT amendEventHandler - replacement in server/Main.hs at line 67
auctionRoute = serveJSON auctionJSON $ method GET auctionGetHandlerauctionBidRoute = serveJSON bidIdJSON $ method POST auctionBidHandlerauctionRoute = serveJSON auctionJSON $ method GET auctionGetHandlerauctionBidRoute = serveJSON bidIdJSON $ method POST auctionBidHandler - replacement in server/Main.hs at line 70
payableRequestsRoute = serveJSON billDetailsJSON $ method GET listPayableRequestsHandlerpaymentRoute = (writeLBS . runPutLazy . encodeMessage =<< method GET getPaymentRequestHandler)<|> (void . method POST . paymentResponseHandler $ billingConfig cfg)billableCreateRoute = serveJSON billableIdJSON $ method POST billableCreateHandlerbillableListRoute = serveJSON (fmap qdbBillableJSON) $ method GET billableListHandlersubscribeRoute = serveJSON subscriptionIdJSON $ method POST subscribeHandler - replacement in server/Main.hs at line 74
amendEventRoute = serveJSON amendmentIdJSON $ method PUT amendEventHandlerpayableRequestsRoute = serveJSON billDetailsJSON $ method GET listPayableRequestsHandlergetPaymentRequestRoute = writeLBS . runPutLazy . encodeMessage =<< method GET getPaymentRequestHandlersubmitPaymentRoute = serveJSON paymentIdJSON $ method POST (paymentResponseHandler $ billingConfig cfg) - replacement in server/Main.hs at line 78
addRoutes [ ("static", serveDirectory $ staticAssetPath cfg)addRoutes [ ("static", serveDirectory . encodeString $ staticAssetPath cfg) - edit in server/Main.hs at line 89
, ("projects/:projectId/auctions", auctionCreateRoute) - edit in server/Main.hs at line 91
, ("projects/:projectId/auctions", auctionCreateRoute) -- <|> auctionListRoute, ("projects/:projectId/billables", billableCreateRoute <|> billableListRoute) - replacement in server/Main.hs at line 96
, ("projects", projectCreateRoute), ("projects", listProjectsRoute), ("projects", projectCreateRoute <|> projectListRoute) - edit in server/Main.hs at line 101
, ("subscribe/:billableId", subscribeRoute) - replacement in server/Main.hs at line 103
, ("pay/:paymentRequestKey", paymentRoute), ("pay/:paymentRequestKey", getPaymentRequestRoute <|> submitPaymentRoute) - replacement in server/Main.hs at line 112
writeLBS =<< (A.encode . f <$> ma)[4.1271]value <- mawriteLBS $ A.encode (f value)