EFSXYZPOGA5M4DN65IEIDO7JK6U34DMQELAPHOIL2UAT6HRC66BAC 4R7XIYK3BP664CO3YJ2VM64ES2JYN27UTQG5KS34OTEPAIODSZLQC IPG33FAWXGEQ2PO6OXRT2PWWXHRNMPVUKKADL6UKKN5GD2CNZ25AC B6HWAPDPXIWH7CHK5VLMWLL6EQN6NOFZEFYO47BPUY2ZO4SL7VDAC EKI57EJR65DA5FPILAHGHHAIU5ITVGHA6V3775OX7GV5XD67OWRQC LAROLAYUGJ4Q5AEFV5EJMIA2ZKBNCBWHHHPCJ3CKCNIUIYUKRFVQC NAS4BFL43SIUAAC663R6VFBHQ2DKI45K6Y6ZKVQI7S5547HBAN7QC NEDDHXUK3GNFMOFO3KLU7NRIHCTYNWBT3D6HTKZAOXFDE6HMDZ6AC IZEVQF627FA7VV25KJAWYWGGC35LZUUBBQRPN5ZAINTQLPEDRTEAC LHJ2HFXVUQ4VG25I7DADWU73G5K5WNZBDQ3SVNKFYLZ5BEYM4XCQC F2XLL7XWGUV4TJD4X2MJADYAQHCSB4HD2TPPEYVHEKHOQIOOFISAC HBULCDN6E75FAPILFVLTQIKABDEWL3HZTBLICLCWOIKDRYM6UIBQC WO2MINIF4TXOHWSE7JWXRZYN64XRVLYIRFMF4SMPSOXKA2V77KMQC GLFF5ZDKWI7WKPZSAEE3IUM27LL6DFOPIL4VPODXYXV3BCSCJ6GQC ZP62WC472OTQETO2HTHIQIPO57XZIWVKPA4KL62GYU4OZDMB6NSAC BSIUHCGFDFDFGWYMHZB7OVU3Z3IHPEUXRISIOPGZI2RUXZFDS2EQC Q5X5RYQLP5K7REYD6VLHOKC4W36ZELJYA45V6YFKTD5S6MPN3NDQC 4FDQGIXN3Z4J55DILCSI5EOLIIA7R5CADTGFMW5X7N7MH6JIMBWAC SEWTRB6S5PO5MQBLCPVBD7XT2BDYNZUE2RO6Z2XENZRIOCN6OZJAC JFOEOFGA4CQR2LW43IVQGDZSPVJAD4KDN2DZMZXGM2QDIUD7AVCAC Y3LIJ5USPMYBG7HKCIQBE7MWVGLQJPJSQD3KPZCTKXP22GOB535QC RN7EI6INGUUHGMNY5RU3NH56WPLRZY5ZMYDNFMNE3TGV4ESFLQIAC 73NDXDEZRMK672GHSTC3CI6YHXFZ2GGJI5IKQGHKFDZKTNSQXLLQC HMDM3B557TO5RYP2IGFFC2C2VN6HYZTDQ47CJY2O37BW55DSMFZAC A6HKMINBNGQLLX4QJMYWKQ4JAEHVJ4HIRVDKPPDI3FJUO2AAB7OQC DFOBMSAODB3NKW37B272ZXA2ML5HMIH3N3C4GT2DPEQS7ZFK4SNAC Z7KS5XHHC6PAMTVBHXY7KUSS3BWAOU6FSYIITUCFOOJZU4OUJHBAC GCVQD44VRPQVKPZEPIC4AOIXLJIG2ZMV3QI2Y7KALUT6NVUBSGSAC 2XQD6KKKD6QVHFHAEMVE3XXY7X2T7BLTLL7XIILZAXNJJH2YONUQC TNR3TEHKVADAEZSTOD2XLSUTSW5AWST2YUW4CWK5KE7DSC6XHZNAC AL37SVTCKRSG4HG2PCYK5Z7QSIZZH5JHH4Q2VLMXFAXSAQRFFG4QC WZFQDWW4XK6M4A4PQ7WQJUTZUPRGQR7V7ZVZY5ZTL5AMGIFMHB2QC O5FVTOM6YFBLEPF3S576K6IMT6ZZ5VQCSB3YVXNS4CKBITKCRZ7AC O227CEAV7BTKSE3SSC7XHC5IWEBXZL2AOOKJMBMOOFNTLINBLQMAC EW2XN7KUMCAQNVFJJ5YTAVDZCPHNWDOEDMRFBUGLY6IE2HKNNX5AC WAIX6AGNDVJOKTWZ7OP7QOYSJHAJSX5EOWXZHOAO2IG6ALWUCJ6QC 3QVT6MA6I2CILQH3LUZABS4YQ7MN6CNRYTDRVS376OOHTPLYTFJAC QADKFHAR3KWQCNYU25Z7PJUGMD5WL26IU3DOAHBTRN2A7NKPUPKAC 64C6AWH66FDKU6UE6Z6JPX2J2GBM2JOPTH2GL6LHKAIUBGNGDZ5AC NLZ3JXLOOIL37O3RRQWXHNPNSNEOOLPD6MCB754BEBECQB3KGR2AC QMRKFEPGFBCEWAIXPEIG5ILKAJ2JH5L3TOITHR4HNJXK5FN3KXBQC 5OI44E4EEVYOMHDWNK2WA7K4L4JWRWCUJUNN2UAUGE5VY4W7GTNAC MB5SHULBN3WP7TGUWZDP6BRGP423FTYKF67T5IF5YHHLNXKQ5REAC FXJQACESPGTLPG5ELXBU3M3OQXUZQQIR7HPIEHQ3FNUTMWVH4WBAC ASF3UPJLCX7KIUCNJD5KAXSPDXCUEJHLL4HBRTRUBPCW73IXOCWQC 4U7F3CPIDTK6JSEDMNMHVKSR7HOQDLZQD2PPVMDLHO5SFSIMUXZAC 4SCFOJGNDAN4XZEAPWQQCBJ3CGZCJP3HUADRQLYZ2ITAKA7EJJTQC EQXRXRZDYCM7BDAVBOXQYPG6C7IJT3OFGNIXCDAHJJBRKAXNGL7AC RPAJLHMTUJU4AYNBOHVGHGGB4NY2NLY3BVPYN5FMWB3ZIMAUQHCQC SCXG6TJWYIPRUMT27KGKIIF6FYKTUTY74UNZ2FQTT63XZ6HIF3AAC N4NDAZYTLSI2W22KT3SYXL257DBMSH3UT2BXOYM7LH7FSZAY4RLAC W35DDBFYF6Z4ZPCFEO5RPAONZLYCSNTXUSTS6FIUUVZHCI6Q7GHAC Z3MK2PJ5U222DXRS22WCDHVPZ7HVAR3HOCUNXIGX6VMEPBQDF6PQC BWN72T44GRRZ6K2OPN56FTLNEB7J7AGC7T2U5HSMLEKUPGJP2NUAC POX3UAMTGCNS3SU5I6IKDNCCSHEAUSFBZ3WCMQ3EXKPNXETOI6BAC 7VGYLTMURLVSVUYFW7TCRZTDQ6RE2EPSPPA43XKHDOBFWYVVSJHQC XTBSG4C7SCZUFOU2BTNFR6B6TCGYI35BWUV4PVTS3N7KNH5VEARQC O722AOKEWXWJPRHGJREU6QPW7HEFPPRETZIAADZ2RMAXHARCNEKAC HALRDT2F22DAQ54M334YSKD2XJCAXDXEZPDKGMOWKH7XELBAS6MAC KEP5WUFJXTMKRRNZLYTGYYWA4VLFCMHTKTJYF5EA5IWBYFMU6WYQC PBD7LZYQHXAA3KLH2ZUX5GW4UFML6BQ32KXZF4KZ6OYFASUYFJ5QC 7HPY3QPFPN35PSPUBVNW2GTFB3CBQZBST4J2BAVJ7QMXLIUN52JAC V2VDN77HCSRYYWXDJJ2XOVHV4P6PVWNJZLXZ7JUYPQEZQIH5BZ3QC GMYPBCWEB6NKURRILAHR3TJUKDOGR2ZMK5I6MS6P5G2LAGH36P3QC 6L5BK5EHPAOQX3JCKUJ273UDNAC23LPQL4HIJGM4AV3P3QK5OKIQC 4B66XH43UYRVNTX57ORJ7U6IJTRFKSUS6IJ3CXVODMEF7NA7UHVQC 5ZSKPQ3KY6T6O5S6T6HW4OHJMQXA72WKJSJJMGKGX2WMFTNZ7EGAC I2KHGVD44KT4MQJXGCTVSQKMBO6TVCY72F26TLXGWRL6PHGF6RNQC 2MNO5FUYXF6GHHWTIDLW2JGMFC3UY54BHJKUYVF7SZCUJQWKZ4DQC DXIGERDTERUIG7QHHRPKTSJHSQEPJPDJVLUW7YVC7URXBQ4ZJVOAC MMRVIM3FRSHP3XB37L7YAQVF4DXDSRRIC5UJS6NSD2LGTQSZJIMQC BROSTG5KP3NUNLSYPVQID254TE47E5RKQAKLPIY7PGWETE6JNMTAC BXGLKYRXO2O4NRM3BLNWQ7AWVPQXAMFS57MFYHJNOZZEZZW5BH6AC UILI6PILCRDPZ3XYA54LGIGPSU7ERWNHCE7R3CE64ZEC7ONOEMOQC KNSI575VAW6HRCZYXOEPQ4DTSML4EORML5MV4DJBRKE7TXCPS4EAC EZQG2APB36DDMIAYDPPDGOIXOD7K2RZZSGC2NKGZIHB2HZBTW7EQC 7KZP4RHZ3QSYTPPQ257A65Z5UPX44TF2LAI2U5EMULQCLDCEUK2AC ADMKQQGCGVSHHIMVQ4XFRDCG544SBJCYALSKZV45CQQBZ4ACUH2AC EMVTF2IWNQGRL44FC4JNG5FYYQTZSFPNM6SOM7IAEH6T7PPK2NVAC HO2PFRABW6BBTE4MUKUTEGXCMJS46WGVBCNWOHO4OL52DVAB4YDAC Z3M53KTLZMPOISMHE25SZJSWX5TA37IV33IRE7KNRAD3PKEAEJXQC 4ZLEDBK7VGLKFUPENAFLUJYNFLKFYJ3TREPQ7P6PKMYGJUXB55HQC PGZJ736CG2E4HXIRYTZTGOMJRX2CHPIFG6H45PPO57EONOWJJ74QC MGOF7IUFGXYQKZOKMM2GGULFFVAULEHLZDSHMUW6B5DBKVXXR74AC UUR6SMCAJMA7O3ZFUCQMPZFDDIPUVQ5IHUAC5F252YVD6H3JIKPQC 3GBSDS5PDSTTJTJOLEKZRRTAONS3T3JFZ3FQGFGS3AOXDBZ6SPLAC NMWWP4ZNOKHZKSJ6F5KYEREWXXR5F4UD35WOKI3EH42AZWVCTCJAC NVOCQVASZWTKQJG7GPH7KHKZZR7NUG4WLV5YY4KAIRPCJRWCZPIAC Y35QCWYW2OTZ27ZVTH2BA3XJTUCJ2WMLKU32ZCOCDY3AW7TIZXRAC 5DRIWGLUKMQZU2ZPBXSTLAWJKAMOD5YXAHM5LEDQHDFGYYLHWCDQC P6NR2CGXCWAW6GXXSIXCGOBIRAS2BM4LEM6D5ADPN4IL7TMW6UVAC OV5AKJHA773ETIJPTMQ7K64U7BRQE34OXJ6FJNH6TZG22WS5QTIAC 2KZPOGRBY6KBMO76F55ZKIVOLSG3O63VP3RHRZVANXYT3OLZ3OWQC WJO37T74RYR5DRMSVNCXAQBOV42FQB63EG43XDZUU5TA354AIJRAC import 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 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 Database.PostgreSQL.Simple (Connection, connect)import Data.Thyme.Clock as Cimport Data.Thyme.Time as Cimport qualified Data.Text as Timport qualified Network.Mail.Mime as Mimeimport qualified Network.Mail.SMTP as SMTPimport Network.URI (URI, parseURI)import Network.Haskoin.Address (Address)import Text.StringTemplate (directoryGroup, newSTMP, getStringTemplate, setManyAttrib, render)import Filesystem.Path.CurrentOS (encodeString)
import Database.PostgreSQL.Simple ( Connection, connect)import Data.Thyme.Clock as Cimport Data.Thyme.Time as Cimport qualified Data.Text as Timport qualified Network.Mail.Mime as Mimeimport qualified Network.Mail.SMTP as SMTPimport Network.URI ( URI, parseURI)import Network.Haskoin.Address ( Address )import Text.StringTemplate ( directoryGroup, newSTMP, getStringTemplate, setManyAttrib, render)import Filesystem.Path.CurrentOS ( encodeString )
import Aftok.Types (User, UserId, ProjectId(..), userEmail, _Email)import Aftok.Currency.Bitcoin (NetworkId, 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, projectName)import qualified AftokD as D
import Aftok.Types ( User, UserId, ProjectId(..), userEmail, _Email)import Aftok.Currency.Bitcoin ( NetworkId, 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, projectName)import qualified AftokD as D
now <- 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)
now <- 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)
let AC.SmtpConfig{..} = cfg ^. (dcfg . D.smtpConfig)preqCfg = cfg ^. (dcfg . D.paymentRequestConfig)reqMay = dopreq <- DB.findPaymentRequestId reqIdpreq' <- traverseOf P.subscription DB.findSubscriptionBillable preq
let AC.SmtpConfig {..} = cfg ^. (dcfg . D.smtpConfig)preqCfg = cfg ^. (dcfg . D.paymentRequestConfig)reqMay = dopreq <- DB.findPaymentRequestId reqIdpreq' <- traverseOf P.subscription DB.findSubscriptionBillable preq
mail <- buildPaymentRequestEmail preqCfg req bip70URLlet mailer = maybe (SMTP.sendMailWithLogin _smtpHost) (SMTP.sendMailWithLogin' _smtpHost) _smtpPort
mail <- buildPaymentRequestEmail preqCfg req bip70URLlet mailer = maybe (SMTP.sendMailWithLogin _smtpHost)(SMTP.sendMailWithLogin' _smtpHost)_smtpPort
buildPaymentRequestEmail :: (MonadIO m, MonadError AftokDErr m)=> D.PaymentRequestConfig-> P.PaymentRequest' (Subscription' (User (NetworkId, Address)) (Billable' Project UserId Satoshi))-> URI-> m Mime.Mail
buildPaymentRequestEmail:: (MonadIO m, MonadError AftokDErr m)=> D.PaymentRequestConfig-> P.PaymentRequest'( Subscription'(User (NetworkId, Address))(Billable' Project UserId Satoshi))-> URI-> m Mime.Mail
toEmail = req ^. (subscription . customer . userEmail)pname = req ^. (subscription . billable . project . projectName)total = req ^. (P.paymentRequest . to paymentRequestTotal)setAttrs = setManyAttrib[ ("from_email", fromEmail ^. _Email)
toEmail = req ^. (subscription . customer . userEmail)pname = req ^. (subscription . billable . project . projectName)total = req ^. (P.paymentRequest . to paymentRequestTotal)setAttrs = setManyAttrib[ ("from_email" , fromEmail ^. _Email)
memoGen :: Subscription' UserId Billable-> C.Day-> C.UTCTime-> AftokM (Maybe Text)
memoGen:: Subscription' UserId Billable -> C.Day -> C.UTCTime -> AftokM (Maybe Text)
import Database.PostgreSQL.Simple (ConnectInfo)import Filesystem.Path.CurrentOS (fromText, encodeString)import qualified Filesystem.Path.CurrentOS as P
import Database.PostgreSQL.Simple ( ConnectInfo )import Filesystem.Path.CurrentOS ( fromText, encodeString)import qualified Filesystem.Path.CurrentOS as P
readConfig 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)
readConfig 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 cfg = PaymentRequestConfig<$> C.require cfg "aftok_host"<*> (fromText <$> C.require cfg "template_path")<*> (Email <$> C.require cfg "payment_from_email")
readPaymentRequestConfig cfg =PaymentRequestConfig<$> C.require cfg "aftok_host"<*> (fromText <$> C.require cfg "template_path")<*> (Email <$> C.require cfg "payment_from_email")
import Control.Exception (try)import System.Environment (getEnv)import System.IO.Error (IOError)import Filesystem.Path.CurrentOS (decodeString)
import Control.Exception ( try )import System.Environment ( getEnv )import System.IO.Error ( IOError )import Filesystem.Path.CurrentOS ( decodeString )
import Data.Hourglass (Seconds(..))import Data.Ratio ((%))import Data.Traversable (for)import Data.Thyme.Clock as Cimport Data.Thyme.Format ()
import Data.Hourglass ( Seconds(..) )import Data.Ratio ( (%) )import Data.Traversable ( for )import Data.Thyme.Clock as Cimport Data.Thyme.Format ( )
bidOrder =comparing costRatio `mappend` comparing (^. bidTime)wheresecs bid = toRational $ bid ^. bidSecondsbtc bid = toRational $ bid ^. bidAmount . satoshicostRatio bid = secs bid / btc bid
bidOrder = comparing costRatio `mappend` comparing (^. bidTime)wheresecs bid = toRational $ bid ^. bidSecondsbtc bid = toRational $ bid ^. bidAmount . satoshicostRatio bid = secs bid / btc bid
let takeWinningBids :: Satoshi -> [Bid] -> [Bid]takeWinningBids total (bid : xs)-- if the total is fully within the raise amount| total <> (bid ^. bidAmount) < raiseAmount' =bid : takeWinningBids (total <> (bid ^. bidAmount)) xs
lettakeWinningBids :: Satoshi -> [Bid] -> [Bid]takeWinningBids total (bid : xs)|-- if the total is fully within the raise amounttotal <> (bid ^. bidAmount) < raiseAmount'= bid : takeWinningBids (total <> (bid ^. bidAmount)) xs|
-- if the last bid will exceed the raise amount, reduce it to fit| total < raiseAmount' =let winFraction r = r % (bid ^. bidAmount . satoshi)remainderSeconds (Satoshi r) = Seconds . round $ winFraction r * fromIntegral (bid ^. bidSeconds)adjustBid r = bid & bidSeconds .~ remainderSeconds r & bidAmount .~ rin toList $ adjustBid <$> raiseAmount' `ssub` total| otherwise = []
-- if the last bid will exceed the raise amount, reduce it to fittotal < raiseAmount'= letwinFraction r = r % (bid ^. bidAmount . satoshi)remainderSeconds (Satoshi r) =Seconds . round $ winFraction r * fromIntegral (bid ^. bidSeconds)adjustBid r = bid & bidSeconds .~ remainderSeconds r & bidAmount .~ rintoList $ adjustBid <$> raiseAmount' `ssub` total| otherwise= []
submittedTotal = bidsTotal bidsin maybe(WinningBids $ takeWinningBids (Satoshi 0) $ sortBy bidOrder bids)InsufficientBids(raiseAmount' `ssub` submittedTotal)
submittedTotal = bidsTotal bidsinmaybe (WinningBids $ takeWinningBids (Satoshi 0) $ sortBy bidOrder bids)InsufficientBids(raiseAmount' `ssub` submittedTotal)
put (x <> bid ^. bidAmount) >>(pure . Just $ Commitment bid (bid ^. bidSeconds) (bid ^. bidAmount))
put (x <> bid ^. bidAmount)>> (pure . Just $ Commitment bid (bid ^. bidSeconds) (bid ^. bidAmount))
import Data.X509.File (readKeyFile, readSignedObject)import Database.PostgreSQL.Simple (ConnectInfo(..))import Filesystem.Path.CurrentOS (fromText, encodeString)import qualified Filesystem.Path.CurrentOS as Pimport Safe (headMay)
import Data.X509.File ( readKeyFile, readSignedObject)import Database.PostgreSQL.Simple ( ConnectInfo(..) )import Filesystem.Path.CurrentOS ( fromText, encodeString)import qualified Filesystem.Path.CurrentOS as Pimport Safe ( headMay )
SmtpConfig <$> C.require cfg "smtpHost"<*> ((fmap . fmap) fromInteger $ C.lookup cfg "smtpPort")<*> C.require cfg "smtpUser"<*> C.require cfg "smtpKey"
SmtpConfig<$> C.require cfg "smtpHost"<*> ((fmap . fmap) fromInteger $ C.lookup cfg "smtpPort")<*> C.require cfg "smtpUser"<*> C.require cfg "smtpKey"
BillingConfig <$> C.require cfg "networkMode"<*> (fromText <$> C.require cfg "signingKeyFile")<*> (fromText <$> C.require cfg "certsFile")<*> C.require cfg "exchangeRateServiceURI"
BillingConfig<$> C.require cfg "networkMode"<*> (fromText <$> C.require cfg "signingKeyFile")<*> (fromText <$> C.require cfg "certsFile")<*> C.require cfg "exchangeRateServiceURI"
ConnectInfo <$> C.require cfg "host"<*> C.require cfg "port"<*> C.require cfg "user"<*> C.require cfg "password"<*> C.require cfg "database"
ConnectInfo<$> C.require cfg "host"<*> C.require cfg "port"<*> C.require cfg "user"<*> C.require cfg "password"<*> C.require cfg "database"
Just _ -> fail $ "Only RSA keys are currently supported for payment request signing."Nothing -> fail $ "No keys found in private key file " <> encodeString (c ^. signingKeyFile)
Just _ ->fail$ "Only RSA keys are currently supported for payment request signing."Nothing -> fail $ "No keys found in private key file " <> encodeString(c ^. signingKeyFile)
import Data.Aeson (FromJSON(..), ToJSON(..))import Aftok.TimeLog.Serialization (depfFromJSON, depfToJSON)import Aftok.Types (DepreciationFunction)
import Data.Aeson ( FromJSON(..), ToJSON(..))import Aftok.TimeLog.Serialization ( depfFromJSON, depfToJSON)import Aftok.Types ( DepreciationFunction )
import Control.Monad.Trans.Except (ExceptT(..), throwE, runExceptT)import Crypto.Random.Types (MonadRandom,getRandomBytes)import Data.Aeson (Value, toJSON)
import Control.Monad.Trans.Except ( ExceptT(..), throwE, runExceptT)import Crypto.Random.Types ( MonadRandom, getRandomBytes)import Data.Aeson ( Value, toJSON)
import qualified Data.List as Limport Data.ProtocolBuffers (decodeMessage,encodeMessage)import Data.Serialize.Get (runGet)import Data.Serialize.Put (runPut)import Data.Thyme.Clock as C
import qualified Data.List as Limport Data.ProtocolBuffers ( decodeMessage, encodeMessage)import Data.Serialize.Get ( runGet )import Data.Serialize.Put ( runPut )import Data.Thyme.Clock as C
import Database.PostgreSQL.Simple.SqlQQ (sql)import Database.PostgreSQL.Simple.Types (Null)import Safe (headMay)
import Database.PostgreSQL.Simple.SqlQQ( sql )import Database.PostgreSQL.Simple.Types( Null )import Safe ( headMay )
import Aftok.Json (billableJSON,createSubscriptionJSON,paymentJSON,paymentRequestJSON)
import Aftok.Json ( billableJSON, createSubscriptionJSON, paymentJSON, paymentRequestJSON)
import Network.Bippy.Types (Satoshi(..))import Network.Haskoin.Address (Address, stringToAddr, addrToString)import Network.Haskoin.Constants (Network)
import Network.Bippy.Types ( Satoshi(..) )import Network.Haskoin.Address ( Address, stringToAddr, addrToString)import Network.Haskoin.Constants ( Network )
else maybe (returnError UnexpectedNull f "event type may not be null")(maybe (returnError Incompatible f "unrecognized event type value") pure . nameEvent . decodeUtf8)v
else maybe(returnError UnexpectedNull f "event type may not be null")( maybe (returnError Incompatible f "unrecognized event type value") pure. nameEvent. decodeUtf8)v
let parser :: Text -> RowParser (CreditTo (NetworkId, Address))parser "credit_to_address" =CreditToCurrency <$> (addressParser mode <* nullField <* nullField)parser "credit_to_user" =CreditToUser <$> (nullField *> nullField *> idParser UserId <* nullField)parser "credit_to_project" =CreditToProject <$> (nullField *> nullField *> nullField *> idParser ProjectId)parser _ = emptyin dotn <- typename fif tn /= "credit_to_t"then returnError Incompatible f "column was not of type credit_to_t"else maybe empty (pure . parser . decodeUtf8) v
letparser :: Text -> RowParser (CreditTo (NetworkId, Address))parser "credit_to_address" =CreditToCurrency <$> (addressParser mode <* nullField <* nullField)parser "credit_to_user" =CreditToUser <$> (nullField *> nullField *> idParser UserId <* nullField)parser "credit_to_project" =CreditToProject<$> (nullField *> nullField *> nullField *> idParser ProjectId)parser _ = emptyindotn <- typename fif tn /= "credit_to_t"then returnError Incompatible f "column was not of type credit_to_t"else maybe empty (pure . parser . decodeUtf8) v
LogEntry <$> creditToParser mode<*> (fieldWith eventTypeParser <*> utcParser)<*> field
LogEntry<$> creditToParser mode<*> (fieldWith eventTypeParser <*> utcParser)<*> field
A.Auction <$> idParser ProjectId<*> idParser UserId<*> utcParser<*> btcParser<*> utcParser<*> utcParser
A.Auction<$> idParser ProjectId<*> idParser UserId<*> utcParser<*> btcParser<*> utcParser<*> utcParser
User <$> (UserName <$> field)<*> ((null *> null *> pure Nothing) <|> fmap Just (addressParser mode))<*> (Email <$> field)
User<$> (UserName <$> field)<*> ((null *> null *> pure Nothing) <|> fmap Just (addressParser mode))<*> (Email <$> field)
P.Project <$> field<*> utcParser<*> idParser UserId<*> (unSerDepFunction <$> fieldWith fromJSONField)
P.Project<$> field<*> utcParser<*> idParser UserId<*> (unSerDepFunction <$> fieldWith fromJSONField)
P.Invitation <$> idParser ProjectId<*> idParser UserId<*> fmap Email field<*> utcParser<*> fmap (fmap toThyme) field
P.Invitation<$> idParser ProjectId<*> idParser UserId<*> fmap Email field<*> utcParser<*> fmap (fmap toThyme) field
B.Billable <$> idParser ProjectId<*> idParser UserId<*> field<*> field<*> recurrenceParser<*> btcParser<*> field<*> fieldWith (optionalField nominalDiffTimeParser)<*> field<*> field
B.Billable<$> idParser ProjectId<*> idParser UserId<*> field<*> field<*> recurrenceParser<*> btcParser<*> field<*> fieldWith (optionalField nominalDiffTimeParser)<*> field<*> field
B.Subscription <$> idParser UserId<*> idParser B.BillableId<*> (toThyme <$> field)<*> ((fmap toThyme) <$> field)
B.Subscription<$> idParser UserId<*> idParser B.BillableId<*> (toThyme <$> field)<*> ((fmap toThyme) <$> field)
PaymentRequest <$> fmap B.SubscriptionId field<*> ((either (const empty) pure . runGet decodeMessage) =<< field)<*> fmap PaymentKey field<*> fmap toThyme field<*> fmap toThyme field
PaymentRequest<$> fmap B.SubscriptionId field<*> ((either (const empty) pure . runGet decodeMessage) =<< field)<*> fmap PaymentKey field<*> fmap toThyme field<*> fmap toThyme field
Payment <$> (PaymentRequestId <$> field)<*> (field >>= (either (const empty) pure . runGet decodeMessage))<*> (toThyme <$> field)<*> field
Payment<$> (PaymentRequestId <$> field)<*> (field >>= (either (const empty) pure . runGet decodeMessage))<*> (toThyme <$> field)<*> field
storeEvent (CreateSubscription uid bid t) =Just $ storeEventJSON (Just uid) "create_subscription" (createSubscriptionJSON uid bid t)
storeEvent (CreateSubscription uid bid t) = Just $ storeEventJSON(Just uid)"create_subscription"(createSubscriptionJSON uid bid t)
pgEval (CreateEvent (ProjectId pid) (UserId uid) (LogEntry c e m)) =case c ofCreditToCurrency (nid, addr) -> domode <- askNetworkModelet network = toNetwork mode nidpinsert EventId[sql| INSERT INTO work_events
pgEval (CreateEvent (ProjectId pid) (UserId uid) (LogEntry c e m)) = case c ofCreditToCurrency (nid, addr) -> domode <- askNetworkModelet network = toNetwork mode nidpinsertEventId[sql| INSERT INTO work_events
let q (Before e) = pquery (logEntryParser mode)[sql| SELECT credit_to_type, credit_to_address, credit_to_user_id, credit_to_project_id,
letq (Before e) = pquery(logEntryParser mode)[sql| SELECT credit_to_type, credit_to_address, credit_to_user_id, credit_to_project_id,
(pid, uid, fromThyme e)q (During s e) = pquery (logEntryParser mode)[sql| SELECT credit_to_type, credit_to_address, credit_to_user_id, credit_to_project_id,
(pid, uid, fromThyme e)q (During s e) = pquery(logEntryParser mode)[sql| SELECT credit_to_type, credit_to_address, credit_to_user_id, credit_to_project_id,
(pid, uid, fromThyme s, fromThyme e)q (After s) = pquery (logEntryParser mode)[sql| SELECT credit_to_type, credit_to_address, credit_to_user_id, credit_to_project_id,
(pid, uid, fromThyme s, fromThyme e)q (After s) = pquery(logEntryParser mode)[sql| SELECT credit_to_type, credit_to_address, credit_to_user_id, credit_to_project_id,
pgEval (AmendEvent (EventId eid) (TimeChange mt t)) =pinsert AmendmentId[sql| INSERT INTO event_time_amendments
pgEval (AmendEvent (EventId eid) (TimeChange mt t)) = pinsertAmendmentId[sql| INSERT INTO event_time_amendments
pgEval (AmendEvent (EventId eid) (MetadataChange mt v)) =pinsert AmendmentId[sql| INSERT INTO event_metadata_amendments
pgEval (AmendEvent (EventId eid) (MetadataChange mt v)) = pinsertAmendmentId[sql| INSERT INTO event_metadata_amendments
pgEval (CreateAuction auc) =pinsert A.AuctionId[sql| INSERT INTO auctions (project_id, initiator_id, raise_amount, end_time)
pgEval (CreateAuction auc) = pinsertA.AuctionId[sql| INSERT INTO auctions (project_id, initiator_id, raise_amount, end_time)
( auc ^. (A.projectId . _ProjectId), auc ^. (A.initiator . _UserId), auc ^. (A.raiseAmount . satoshi), auc ^. (A.auctionEnd . to fromThyme))
( auc ^. (A.projectId . _ProjectId), auc ^. (A.initiator . _UserId), auc ^. (A.raiseAmount . satoshi), auc ^. (A.auctionEnd . to fromThyme))
pgEval (FindAuction aucId) =headMay <$> pquery auctionParser[sql| SELECT project_id, initiator_id, created_at, raise_amount, start_time, end_time
pgEval (FindAuction aucId) = headMay <$> pqueryauctionParser[sql| SELECT project_id, initiator_id, created_at, raise_amount, start_time, end_time
pgEval (CreateBid (A.AuctionId aucId) bid) =pinsert A.BidId[sql| INSERT INTO bids (auction_id, bidder_id, bid_seconds, bid_amount, bid_time)
pgEval (CreateBid (A.AuctionId aucId) bid) = pinsertA.BidId[sql| INSERT INTO bids (auction_id, bidder_id, bid_seconds, bid_amount, bid_time)
( aucId, bid ^. (A.bidUser . _UserId), case bid ^. A.bidSeconds of (Seconds i) -> i, bid ^. (A.bidAmount . satoshi), bid ^. (A.bidTime . to fromThyme))
( aucId, bid ^. (A.bidUser . _UserId), case bid ^. A.bidSeconds of(Seconds i) -> i, bid ^. (A.bidAmount . satoshi), bid ^. (A.bidTime . to fromThyme))
pgEval (FindBids aucId) =pquery ((,) <$> idParser A.BidId <*> bidParser)[sql| SELECT id, bidder_id, bid_seconds, bid_amount, bid_time FROM bids WHERE auction_id = ? |](Only (aucId ^. A._AuctionId))
pgEval (FindBids aucId) = pquery((,) <$> idParser A.BidId <*> bidParser)[sql| SELECT id, bidder_id, bid_seconds, bid_amount, bid_time FROM bids WHERE auction_id = ? |](Only (aucId ^. A._AuctionId))
pgEval (FindInvitation ic) =headMay <$> pquery invitationParser[sql| SELECT project_id, invitor_id, invitee_email, invitation_time, acceptance_time
pgEval (FindInvitation ic) = headMay <$> pqueryinvitationParser[sql| SELECT project_id, invitor_id, invitee_email, invitation_time, acceptance_time
pgEval (CreateProject p) =pinsert ProjectId[sql| INSERT INTO projects (project_name, inception_date, initiator_id, depreciation_fn)
pgEval (CreateProject p) = pinsertProjectId[sql| INSERT INTO projects (project_name, inception_date, initiator_id, depreciation_fn)
( p ^. P.projectName, p ^. (P.inceptionDate . to fromThyme), p ^. (P.initiator . _UserId), toJSON $ p ^. P.depf . to SerDepFunction)
( p ^. P.projectName, p ^. (P.inceptionDate . to fromThyme), p ^. (P.initiator . _UserId), toJSON $ p ^. P.depf . to SerDepFunction)
pgEval (FindProject (ProjectId pid)) =headMay <$> pquery projectParser[sql| SELECT project_name, inception_date, initiator_id, depreciation_fn FROM projects WHERE id = ? |](Only pid)
pgEval (FindProject (ProjectId pid)) = headMay <$> pqueryprojectParser[sql| SELECT project_name, inception_date, initiator_id, depreciation_fn FROM projects WHERE id = ? |](Only pid)
pgEval (FindUserProjects (UserId uid)) =pquery ((,) <$> idParser ProjectId <*> projectParser)[sql| SELECT p.id, p.project_name, p.inception_date, p.initiator_id, p.depreciation_fn
pgEval (FindUserProjects (UserId uid)) = pquery((,) <$> idParser ProjectId <*> projectParser)[sql| SELECT p.id, p.project_name, p.inception_date, p.initiator_id, p.depreciation_fn
pgEval (AddUserToProject pid current new) = void $pexec[sql| INSERT INTO project_companions (project_id, user_id, invited_by) VALUES (?, ?, ?) |](pid ^. _ProjectId, new ^. _UserId, current ^. _UserId)
pgEval (AddUserToProject pid current new) = void $ pexec[sql| INSERT INTO project_companions (project_id, user_id, invited_by) VALUES (?, ?, ?) |](pid ^. _ProjectId, new ^. _UserId, current ^. _UserId)
pgEval (FindBillable bid) =headMay <$> pquery billableParser[sql| SELECT b.project_id, e.created_by, b.name, b.description,
pgEval (FindBillable bid) = headMay <$> pquerybillableParser[sql| SELECT b.project_id, e.created_by, b.name, b.description,
pgEval (FindBillables pid) =pquery ((,) <$> idParser B.BillableId <*> billableParser)[sql| SELECT b.id, b.project_id, e.created_by, b.name, b.description,
pgEval (FindBillables pid) = pquery((,) <$> idParser B.BillableId <*> billableParser)[sql| SELECT b.id, b.project_id, e.created_by, b.name, b.description,
pgEval (FindSubscription sid) =headMay <$> pquery subscriptionParser[sql| SELECT id, billable_id, start_date, end_date
pgEval (FindSubscription sid) = headMay <$> pquerysubscriptionParser[sql| SELECT id, billable_id, start_date, end_date
pgEval (FindSubscriptions uid pid) =pquery ((,) <$> idParser B.SubscriptionId <*> subscriptionParser)[sql| SELECT s.id, user_id, billable_id, start_date, end_date
pgEval (FindSubscriptions uid pid) = pquery((,) <$> idParser B.SubscriptionId <*> subscriptionParser)[sql| SELECT s.id, user_id, billable_id, start_date, end_date
pgEval (FindPaymentRequest (PaymentKey k)) =headMay <$> pquery ((,) <$> idParser PaymentRequestId <*> paymentRequestParser)
pgEval (FindPaymentRequest (PaymentKey k)) = headMay <$> pquery((,) <$> idParser PaymentRequestId <*> paymentRequestParser)
rowp = (,,,) <$> (PaymentKey <$> field)<*> paymentRequestParser<*> subscriptionParser<*> billableParserin pquery rowp[sql| SELECT r.url_key,
rowp =(,,,)<$> (PaymentKey <$> field)<*> paymentRequestParser<*> subscriptionParser<*> billableParserin pqueryrowp[sql| SELECT r.url_key,
import Control.Lens (view, (^.), makeClassyPrisms, traverseOf)import Control.Monad.Trans.Maybe (MaybeT (..))
import Control.Lens ( view, (^.), makeClassyPrisms, traverseOf)import Control.Monad.Trans.Maybe ( MaybeT(..) )
CreateUser :: BTCUser -> DBOp UserIdFindUser :: UserId -> DBOp (Maybe BTCUser)FindUserByName :: UserName -> DBOp (Maybe (UserId, BTCUser))
CreateUser ::BTCUser -> DBOp UserIdFindUser ::UserId -> DBOp (Maybe BTCUser)FindUserByName ::UserName -> DBOp (Maybe (UserId, BTCUser))
CreateProject :: Project -> DBOp ProjectIdFindProject :: ProjectId -> DBOp (Maybe Project)ListProjects :: DBOp [ProjectId]FindSubscribers :: ProjectId -> DBOp [UserId]FindUserProjects :: UserId -> DBOp [(ProjectId, Project)]AddUserToProject :: ProjectId -> InvitingUID -> InvitedUID -> DBOp ()CreateInvitation :: ProjectId -> InvitingUID -> Email -> C.UTCTime -> DBOp InvitationCodeFindInvitation :: InvitationCode -> DBOp (Maybe Invitation)AcceptInvitation :: UserId -> InvitationCode -> C.UTCTime -> DBOp ()
CreateProject ::Project -> DBOp ProjectIdFindProject ::ProjectId -> DBOp (Maybe Project)ListProjects ::DBOp [ProjectId]FindSubscribers ::ProjectId -> DBOp [UserId]FindUserProjects ::UserId -> DBOp [(ProjectId, Project)]AddUserToProject ::ProjectId -> InvitingUID -> InvitedUID -> DBOp ()CreateInvitation ::ProjectId -> InvitingUID -> Email -> C.UTCTime -> DBOp InvitationCodeFindInvitation ::InvitationCode -> DBOp (Maybe Invitation)AcceptInvitation ::UserId -> InvitationCode -> C.UTCTime -> DBOp ()
CreateEvent :: ProjectId -> UserId -> LogEntry BTCNet -> DBOp EventIdAmendEvent :: EventId -> EventAmendment BTCNet -> DBOp AmendmentIdFindEvent :: EventId -> DBOp (Maybe (KeyedLogEntry BTCNet))FindEvents :: ProjectId -> UserId -> Interval' -> DBOp [LogEntry BTCNet]ReadWorkIndex :: ProjectId -> DBOp (WorkIndex BTCNet)
CreateEvent ::ProjectId -> UserId -> LogEntry BTCNet -> DBOp EventIdAmendEvent ::EventId -> EventAmendment BTCNet -> DBOp AmendmentIdFindEvent ::EventId -> DBOp (Maybe (KeyedLogEntry BTCNet))FindEvents ::ProjectId -> UserId -> Interval' -> DBOp [LogEntry BTCNet]ReadWorkIndex ::ProjectId -> DBOp (WorkIndex BTCNet)
CreateAuction :: Auction -> DBOp AuctionIdFindAuction :: AuctionId -> DBOp (Maybe Auction)CreateBid :: AuctionId -> Bid -> DBOp BidIdFindBids :: AuctionId -> DBOp [(BidId, Bid)]
CreateAuction ::Auction -> DBOp AuctionIdFindAuction ::AuctionId -> DBOp (Maybe Auction)CreateBid ::AuctionId -> Bid -> DBOp BidIdFindBids ::AuctionId -> DBOp [(BidId, Bid)]
CreateBillable :: UserId -> Billable -> DBOp BillableIdFindBillable :: BillableId -> DBOp (Maybe Billable)FindBillables :: ProjectId -> DBOp [(BillableId, Billable)]
CreateBillable ::UserId -> Billable -> DBOp BillableIdFindBillable ::BillableId -> DBOp (Maybe Billable)FindBillables ::ProjectId -> DBOp [(BillableId, Billable)]
CreateSubscription :: UserId -> BillableId -> T.Day -> DBOp SubscriptionIdFindSubscription :: SubscriptionId -> DBOp (Maybe Subscription)FindSubscriptions :: UserId -> ProjectId -> DBOp [(SubscriptionId, Subscription)]
CreateSubscription ::UserId -> BillableId -> T.Day -> DBOp SubscriptionIdFindSubscription ::SubscriptionId -> DBOp (Maybe Subscription)FindSubscriptions ::UserId -> ProjectId -> DBOp [(SubscriptionId, Subscription)]
CreatePaymentRequest :: PaymentRequest -> DBOp PaymentRequestIdFindPaymentRequests :: SubscriptionId -> DBOp [(PaymentRequestId, PaymentRequest)]FindUnpaidRequests :: SubscriptionId -> DBOp [BillDetail]FindPaymentRequest :: PaymentKey -> DBOp (Maybe (PaymentRequestId, PaymentRequest))FindPaymentRequestId :: PaymentRequestId -> DBOp (Maybe PaymentRequest)
CreatePaymentRequest ::PaymentRequest -> DBOp PaymentRequestIdFindPaymentRequests ::SubscriptionId -> DBOp [(PaymentRequestId, PaymentRequest)]FindUnpaidRequests ::SubscriptionId -> DBOp [BillDetail]FindPaymentRequest ::PaymentKey -> DBOp (Maybe (PaymentRequestId, PaymentRequest))FindPaymentRequestId ::PaymentRequestId -> DBOp (Maybe PaymentRequest)
CreatePayment :: Payment -> DBOp PaymentIdFindPayments :: PaymentRequestId -> DBOp [(PaymentId, Payment)]
CreatePayment ::Payment -> DBOp PaymentIdFindPayments ::PaymentRequestId -> DBOp [(PaymentId, Payment)]
auc <- MaybeT $ liftdb findOp_ <- lift $ checkProjectAuth (auc ^. A.projectId) uid findOppure auc
auc <- MaybeT $ liftdb findOp_ <- lift $ checkProjectAuth (auc ^. A.projectId) uid findOppure auc
maybeAuc <- liftdb findOp_ <- traverse (\auc -> checkProjectAuth (auc ^. A.projectId) uid findOp) maybeAucmaybe (raiseSubjectNotFound findOp) pure maybeAuc
maybeAuc <- liftdb findOp_ <- traverse(\auc -> checkProjectAuth (auc ^. A.projectId) uid findOp)maybeAucmaybe (raiseSubjectNotFound findOp) pure maybeAuc
auc <- findAuction' aid uidif view bidTime bid > view auctionEnd aucthen raiseOpForbidden uid AuctionEnded createOpelse liftdb createOp
auc <- findAuction' aid uidif view bidTime bid > view auctionEnd aucthen raiseOpForbidden uid AuctionEnded createOpelse liftdb createOp
( Interval(..), interval, start, end, ilen, Interval'(..), interval', start', end', intervalJSON, parseIntervalJSON
( Interval(..), interval, start, end, ilen, Interval'(..), interval', start', end', intervalJSON, parseIntervalJSON
import Control.FromSum (fromMaybeM, fromEitherM)import Control.Lens hiding ((.=))import Control.Monad.Fail (MonadFail(..))
import Control.FromSum ( fromMaybeM, fromEitherM)import Control.Lens hiding ( (.=) )import Control.Monad.Fail ( MonadFail(..) )
import qualified Data.Attoparsec.ByteString.Char8 as PCimport qualified Data.ByteString.Base64 as B64import qualified Data.ByteString.Char8 as C
import qualified Data.Attoparsec.ByteString.Char8as PCimport qualified Data.ByteString.Base64 as B64import qualified Data.ByteString.Char8 as C
import Data.HashMap.Strict as Oimport Data.List.NonEmpty as Limport Data.Map.Strict as MSimport Data.ProtocolBuffers (encodeMessage)import Data.Serialize.Put (runPut)import qualified Data.Text as Timport qualified Data.Text.Encoding as Timport Data.Thyme.Calendar (showGregorian)import Data.Thyme.Clock as Clockimport Data.Thyme.Time (Day)import Data.UUID as U
import Data.HashMap.Strict as Oimport Data.List.NonEmpty as Limport Data.Map.Strict as MSimport Data.ProtocolBuffers ( encodeMessage )import Data.Serialize.Put ( runPut )import qualified Data.Text as Timport qualified Data.Text.Encoding as Timport Data.Thyme.Calendar ( showGregorian )import Data.Thyme.Clock as Clockimport Data.Thyme.Time ( Day )import Data.UUID as U
v = QuasiQuoter { quoteExp = quoteVersionExp, quotePat = error "Pattern quasiquotation of versions not supported.", quoteType = error "Type quasiquotation of versions not supported.", quoteDec = error "Dec quasiquotation of versions not supported."}
v = QuasiQuoter{ quoteExp = quoteVersionExp, quotePat = error "Pattern quasiquotation of versions not supported.", quoteType = error "Type quasiquotation of versions not supported.", quoteDec = error "Dec quasiquotation of versions not supported."}
projectJSON p = v1 $obj [ "projectName" .= (p ^. projectName), "inceptionDate" .= (p ^. inceptionDate), "initiator" .= (p ^. P.initiator . _UserId)]
projectJSON p = v1 $ obj[ "projectName" .= (p ^. projectName), "inceptionDate" .= (p ^. inceptionDate), "initiator" .= (p ^. P.initiator . _UserId)]
auctionJSON x = v1 $obj [ "projectId" .= idValue (A.projectId._ProjectId) x, "initiator" .= idValue (A.initiator._UserId) x, "raiseAmount" .= (x ^. (raiseAmount . satoshi))]
auctionJSON x = v1 $ obj[ "projectId" .= idValue (A.projectId . _ProjectId) x, "initiator" .= idValue (A.initiator . _UserId) x, "raiseAmount" .= (x ^. (raiseAmount . satoshi))]
creditToJSON nmode (CreditToCurrency (netId, addr)) =v2 $ obj [ "creditToAddress" .= addrToJSON (toNetwork nmode netId) addr, "creditToNetwork" .= renderNetworkId netId]
creditToJSON nmode (CreditToCurrency (netId, addr)) = v2 $ obj[ "creditToAddress" .= addrToJSON (toNetwork nmode netId) addr, "creditToNetwork" .= renderNetworkId netId]
:: NetworkMode-> NetworkId-> Text-> Parser (CreditTo (NetworkId, Address))parseBtcAddr nmode net addrText =maybe(fail . T.unpack $ "Address " <> addrText <> " cannot be parsed as a BTC network address.")(pure . CreditToCurrency . (net,))(stringToAddr (toNetwork nmode net) addrText)
:: NetworkMode -> NetworkId -> Text -> Parser (CreditTo (NetworkId, Address))parseBtcAddr nmode net addrText = maybe( fail. T.unpack$ "Address "<> addrText<> " cannot be parsed as a BTC network address.")(pure . CreditToCurrency . (net, ))(stringToAddr (toNetwork nmode net) addrText)
let parseCreditToAddr = donetName <- o .: "creditToNetwork"net <- fromMaybeM(fail . T.unpack $ "Currency network " <> netName <> " not recognized.")(parseNetworkId netName)addrValue <- o .: "creditToAddress"CreditToCurrency . (net,) <$> addrFromJSON (toNetwork nmode net) addrValue
letparseCreditToAddr = donetName <- o .: "creditToNetwork"net <- fromMaybeM(fail . T.unpack $ "Currency network " <> netName <> " not recognized.")(parseNetworkId netName)addrValue <- o .: "creditToAddress"CreditToCurrency. (net, )<$> addrFromJSON (toNetwork nmode net) addrValue
payoutsJSON :: NetworkMode -> Payouts (NetworkId, Address)-> ValuepayoutsJSON nmode (Payouts m) = v2 $let payoutsRec :: (CreditTo (NetworkId, Address), Rational) -> ValuepayoutsRec (c, r) = object [ "creditTo" .= creditToJSON nmode c, "payoutRatio" .= r]in obj $ [ "payouts" .= fmap payoutsRec (MS.assocs m) ]
payoutsJSON :: NetworkMode -> Payouts (NetworkId, Address) -> ValuepayoutsJSON nmode (Payouts m) =v2$ let payoutsRec :: (CreditTo (NetworkId, Address), Rational) -> ValuepayoutsRec (c, r) =object ["creditTo" .= creditToJSON nmode c, "payoutRatio" .= r]in obj $ ["payouts" .= fmap payoutsRec (MS.assocs m)]
let parsePayoutRecord x = (,) <$> (parseCreditToV2 nmode =<< (x .: "creditTo"))<*> (x .: "payoutRatio")in Payouts . MS.fromList <$> (traverse parsePayoutRecord =<< parseJSON (Object val))
let parsePayoutRecord x =(,)<$> (parseCreditToV2 nmode =<< (x .: "creditTo"))<*> (x .: "payoutRatio")in Payouts. MS.fromList<$> (traverse parsePayoutRecord =<< parseJSON (Object val))
workIndexJSON nmode (WorkIndex widx) = v2 $let widxRec :: (CreditTo (NetworkId, Address), NonEmpty Interval) -> ValuewidxRec (c, l) = object [ "creditTo" .= creditToJSON nmode c, "intervals" .= (intervalJSON <$> L.toList l)]in obj $ [ "workIndex" .= fmap widxRec (MS.assocs widx) ]
workIndexJSON nmode (WorkIndex widx) =v2$ let widxRec :: (CreditTo (NetworkId, Address), NonEmpty Interval) -> ValuewidxRec (c, l) = object[ "creditTo" .= creditToJSON nmode c, "intervals" .= (intervalJSON <$> L.toList l)]in obj $ ["workIndex" .= fmap widxRec (MS.assocs widx)]
logEntryJSON nmode (LogEntry c ev m) = v2 $obj [ "creditTo" .= creditToJSON nmode c, "event" .= logEventJSON' ev, "eventMeta" .= m]
logEntryJSON nmode (LogEntry c ev m) = v2 $ obj[ "creditTo" .= creditToJSON nmode c, "event" .= logEventJSON' ev, "eventMeta" .= m]
recurrenceJSON' B.Annually = object [ "annually" .= Null ]recurrenceJSON' (B.Monthly i) = object [ "monthly " .= object [ "months" .= i ] ]
recurrenceJSON' B.Annually = object ["annually" .= Null]recurrenceJSON' (B.Monthly i) = object ["monthly " .= object ["months" .= i]]
recurrenceJSON' (B.Weekly i) = object [ "weekly " .= object [ "weeks" .= i ] ]recurrenceJSON' B.OneTime = object [ "onetime" .= Null ]
recurrenceJSON' (B.Weekly i) = object ["weekly " .= object ["weeks" .= i]]recurrenceJSON' B.OneTime = object ["onetime" .= Null]
createSubscriptionJSON uid bid d = v1 $obj [ "user_id" .= idValue _UserId uid, "billable_id" .= idValue B._BillableId bid, "start_date" .= showGregorian d]
createSubscriptionJSON uid bid d = v1 $ obj[ "user_id" .= idValue _UserId uid, "billable_id" .= idValue B._BillableId bid, "start_date" .= showGregorian d]
billDetailJSON r =obj $ concat[ ["payment_request_id" .= view (_1 . _PaymentKey) r], paymentRequestKV $ view _2 r, subscriptionKV $ view _3 r, billableKV $ view _4 r]
billDetailJSON r = obj $ concat[ ["payment_request_id" .= view (_1 . _PaymentKey) r], paymentRequestKV $ view _2 r, subscriptionKV $ view _3 r, billableKV $ view _4 r]
paymentJSON r = v1 $obj [ "payment_request_id" .= idValue (request . _PaymentRequestId) r, "payment_protobuf_64" .= view paymentBytes r, "payment_date" .= (r ^. paymentDate)]wherepaymentBytes = payment . to (T.decodeUtf8 . B64.encode . runPut . encodeMessage)
paymentJSON r = v1 $ obj[ "payment_request_id" .= idValue (request . _PaymentRequestId) r, "payment_protobuf_64" .= view paymentBytes r, "payment_date" .= (r ^. paymentDate)]wherepaymentBytes =payment . to (T.decodeUtf8 . B64.encode . runPut . encodeMessage)
let parseA :: Text -> Parser (EventAmendment (NetworkId, Address))parseA "timeChange" = TimeChange t <$> o .: "eventTime"parseA "addrChange" = CreditToChange t <$> parseCreditToV1 nmode oparseA "metadataChange" = MetadataChange t <$> o .: "eventMeta"parseA tid = fail . T.unpack $ "Amendment type " <> tid <> " not recognized."in o .: "amendment" >>= parseA
letparseA :: Text -> Parser (EventAmendment (NetworkId, Address))parseA "timeChange" = TimeChange t <$> o .: "eventTime"parseA "addrChange" = CreditToChange t <$> parseCreditToV1 nmode oparseA "metadataChange" = MetadataChange t <$> o .: "eventMeta"parseA tid =fail . T.unpack $ "Amendment type " <> tid <> " not recognized."ino .: "amendment" >>= parseA
let parseA :: Text -> Parser (EventAmendment (NetworkId, Address))parseA "timeChange" = TimeChange t <$> o .: "eventTime"parseA "creditToChange" = CreditToChange t <$> parseCreditToV2 nmode oparseA "metadataChange" = MetadataChange t <$> o .: "eventMeta"parseA tid = fail . T.unpack $ "Amendment type " <> tid <> " not recognized."in o .: "amendment" >>= parseA
letparseA :: Text -> Parser (EventAmendment (NetworkId, Address))parseA "timeChange" = TimeChange t <$> o .: "eventTime"parseA "creditToChange" = CreditToChange t <$> parseCreditToV2 nmode oparseA "metadataChange" = MetadataChange t <$> o .: "eventMeta"parseA tid =fail . T.unpack $ "Amendment type " <> tid <> " not recognized."ino .: "amendment" >>= parseA
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'
letparseAnnually 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'
notFound = fail $ "Value " <> show o <> " does not represent a Recurrence value."parseV val = parseAnnually val<|> parseMonthly val<|> parseWeekly val<|> parseOneTime valin fromMaybe notFound $ parseV o
notFound =fail $ "Value " <> show o <> " does not represent a Recurrence value."parseV val =parseAnnually val<|> parseMonthly val<|> parseWeekly val<|> parseOneTime valinfromMaybe notFound $ parseV o
import Data.Aeson (Value)import Data.Thyme.Clock as Cimport Data.Thyme.Time as Cimport qualified Data.Text as T
import Data.Aeson ( Value )import Data.Thyme.Clock as Cimport Data.Thyme.Time as Cimport qualified Data.Text as T
import qualified Network.Bippy.Proto as Pimport Network.Bippy.Types (expiryTime, getExpires,getPaymentDetails, Satoshi(..))import Network.Haskoin.Address.Base58 (decodeBase58Check)
import qualified Network.Bippy.Proto as Pimport Network.Bippy.Types ( expiryTime, getExpires, getPaymentDetails, Satoshi(..))import Network.Haskoin.Address.Base58 ( decodeBase58Check )
-- using error here is reasonable since it would indicate-- a serialization problemin either (error . T.pack) (check . getExpires) $ getPaymentDetails (view paymentRequest req)
-- using error here is reasonable since it would indicate-- a serialization problemin either (error . T.pack) (check . getExpires)$ getPaymentDetails (view paymentRequest req)
import Control.Lens (makeLenses, makePrisms)import Crypto.Random.Types (MonadRandom, getRandomBytes)
import Control.Lens ( makeLenses, makePrisms)import Crypto.Random.Types ( MonadRandom, getRandomBytes)
import qualified Data.ByteString as BSimport Data.ByteString.Base64.URL as B64import Data.Thyme.Clock as C
import qualified Data.ByteString as BSimport Data.ByteString.Base64.URL as B64import Data.Thyme.Clock as C
import Data.Aeson (Value(..), (.=), (.:), object)import Data.Aeson.Types (Parser)import Data.Functor ((<$>))import Data.Text (unpack)
import Data.Aeson ( Value(..), (.=), (.:), object)import Data.Aeson.Types ( Parser )import Data.Functor ( (<$>) )import Data.Text ( unpack )
LinearDepreciation (Months up) (Months dp) ->object [ "type" .= ("LinearDepreciation" :: Text), "arguments" .= object [ "undep" .= up, "dep" .= dp]]
LinearDepreciation (Months up) (Months dp) -> object[ "type" .= ("LinearDepreciation" :: Text), "arguments" .= object ["undep" .= up, "dep" .= dp]]
import Control.Lens (makeLenses, makePrisms)import Data.Maybe (Maybe)import Data.Eq (Eq)import Data.Functor (Functor)import Data.Ord (Ord)import Data.Text (Text)import Data.UUID (UUID)import Prelude (Integer)import Text.Show (Show)
import Control.Lens ( makeLenses, makePrisms)import Data.Maybe ( Maybe )import Data.Eq ( Eq )import Data.Functor ( Functor )import Data.Ord ( Ord )import Data.Text ( Text )import Data.UUID ( UUID )import Prelude ( Integer )import Text.Show ( Show )
import System.Environment (getEnvironment)import Filesystem.Path.CurrentOS (fromText, encodeString)import qualified Filesystem.Path.CurrentOS as P
import System.Environment ( getEnvironment )import Filesystem.Path.CurrentOS ( fromText, encodeString)import qualified Filesystem.Path.CurrentOS as P
QConfig <$> C.lookupDefault "localhost" cfg "hostname"<*> C.lookupDefault 8000 cfg "port"<*> (fromText <$> C.require cfg "siteKey")<*> C.lookup cfg "cookieTimeout"<*> maybe (mkPGSConfig $ C.subconfig "db" cfg) pure pc<*> readSmtpConfig cfg<*> (readBillingConfig $ C.subconfig "billing" cfg)<*> (fromText <$> C.lookupDefault "/opt/aftok/server/templates/" cfg "templatePath")<*> (fromText <$> C.lookupDefault "/opt/aftok/server/static/" cfg "staticAssetPath")
QConfig<$> C.lookupDefault "localhost" cfg "hostname"<*> C.lookupDefault 8000 cfg "port"<*> (fromText <$> C.require cfg "siteKey")<*> C.lookup cfg "cookieTimeout"<*> maybe (mkPGSConfig $ C.subconfig "db" cfg) pure pc<*> readSmtpConfig cfg<*> (readBillingConfig $ C.subconfig "billing" cfg)<*> (fromText <$> C.lookupDefault "/opt/aftok/server/templates/"cfg"templatePath")<*> (fromText <$> C.lookupDefault "/opt/aftok/server/static/"cfg"staticAssetPath")
import Aftok.Types (UserId)import Aftok.Auction (Auction (..), AuctionId, Bid (..), BidId)import Aftok.Database (createAuction, createBid, findAuction)
import Aftok.Types ( UserId )import Aftok.Auction ( Auction(..), AuctionId, Bid(..), BidId)import Aftok.Database ( createAuction, createBid, findAuction)
snapEval . createAuction $Auction pid uid t (Satoshi . raiseAmount $ req) (auctionStart req) (auctionEnd req)
snapEval . createAuction $ Auction piduidt(Satoshi . raiseAmount $ req)(auctionStart req)(auctionEnd req)
fromMaybeT(snapError 404 $ "Auction not found for id " <> show aid)(mapMaybeT snapEval $ findAuction aid uid) -- this will verify auction access
fromMaybeT (snapError 404 $ "Auction not found for id " <> show aid)(mapMaybeT snapEval $ findAuction aid uid) -- this will verify auction access
import Control.Error.Util (maybeT)import Control.Monad.Trans.Maybe (mapMaybeT)import Data.Attoparsec.ByteString (parseOnly)
import Control.Error.Util ( maybeT )import Control.Monad.Trans.Maybe ( mapMaybeT )import Data.Attoparsec.ByteString ( parseOnly )
(uname, pwd) <- either (throwDenied . AU.AuthError) pure $ parseOnly authHeaderParser rawHeaderauthResult <- with auth $ AU.loginByUsername uname (AU.ClearText pwd) False
(uname, pwd) <- either (throwDenied . AU.AuthError) pure$ parseOnly authHeaderParser rawHeaderauthResult <- with auth $ AU.loginByUsername uname (AU.ClearText pwd) False
modifyResponse $ (setResponseStatus 401 "Unauthorized") .(setHeader "WWW-Authenticate" "Basic realm=aftok")getResponse >>= finishWith
modifyResponse$ (setResponseStatus 401 "Unauthorized"). (setHeader "WWW-Authenticate" "Basic realm=aftok")getResponse >>= finishWith
modifyResponse $ setResponseStatus 403 "Access Denied"writeText $ "Access Denied: " <> show failuregetResponse >>= finishWith
modifyResponse $ setResponseStatus 403 "Access Denied"writeText $ "Access Denied: " <> show failuregetResponse >>= finishWith
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"
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"
import Control.Lens (view, _1, _2, _Right, _Left, preview, (&), (.~), (^.))import Control.Monad.Trans.Maybe (mapMaybeT)import Control.Exception (try)
import Control.Lens ( view, _1, _2, _Right, _Left, preview, (&), (.~), (^.))import Control.Monad.Trans.Maybe ( mapMaybeT )import Control.Exception ( try )
import Data.ProtocolBuffers (decodeMessage)import Data.Serialize.Get (runGetLazy)import Data.Thyme.Clock as Cimport qualified Data.Text.Encoding as Timport qualified Network.Bippy.Proto as P
import Data.ProtocolBuffers ( decodeMessage )import Data.Serialize.Get ( runGetLazy )import Data.Thyme.Clock as Cimport qualified Data.Text.Encoding as Timport qualified Network.Bippy.Proto as P
import Network.HTTP.Client (defaultManagerSettings, managerResponseTimeout, responseTimeoutMicro, HttpException)import Network.Wreq (asValue, responseBody, defaults, manager, getWith)import OpenSSL.Session (context)
import Network.HTTP.Client ( defaultManagerSettings, managerResponseTimeout, responseTimeoutMicro, HttpException)import Network.Wreq ( asValue, responseBody, defaults, manager, getWith)import OpenSSL.Session ( context )
preq <- getPaymentRequestHandler'pmnt <- either(\msg -> snapError 400 $ "Could not decode payment response: " <> show msg)pure(runGetLazy decodeMessage requestBody)now <- liftIO $ C.getCurrentTime
preq <- getPaymentRequestHandler'pmnt <- either(\msg -> snapError 400 $ "Could not decode payment response: " <> show msg)pure(runGetLazy decodeMessage requestBody)now <- liftIO $ C.getCurrentTime
let opts = defaults & manager .~ Left (opensslManagerSettings context)& manager .~ Left (defaultManagerSettings { managerResponseTimeout = responseTimeoutMicro 10000 } )
letopts =defaults& manager.~ Left (opensslManagerSettings context)& manager.~ Left(defaultManagerSettings{ managerResponseTimeout = responseTimeoutMicro 10000})
pkey <- maybe(snapError 400 $ "parameter paymentRequestKey is formatted incorrectly.") pure(parsePaymentKey pkBytes)
pkey <- maybe(snapError 400 $ "parameter paymentRequestKey is formatted incorrectly.")pure(parsePaymentKey pkBytes)
import Control.Monad.Trans.Maybe (mapMaybeT, runMaybeT)import Data.Aeson as Aimport Data.Attoparsec.ByteString (takeByteString)import Data.Thyme.Clock as Cimport Filesystem.Path.CurrentOS (encodeString)import qualified Filesystem.Path.CurrentOS as F
import Control.Monad.Trans.Maybe ( mapMaybeT, runMaybeT)import Data.Aeson as Aimport Data.Attoparsec.ByteString ( takeByteString )import Data.Thyme.Clock as Cimport Filesystem.Path.CurrentOS ( encodeString )import qualified Filesystem.Path.CurrentOS as F
cp <- either (snapError 400 . show) pure $ A.eitherDecode requestBodyt <- liftIO C.getCurrentTime
cp <- either (snapError 400 . show) pure $ A.eitherDecode requestBodyt <- liftIO C.getCurrentTime
fromMaybeT(snapError 404 $ "Project not found for id " <> show pid)(mapMaybeT snapEval $ findUserProject uid pid)
fromMaybeT (snapError 404 $ "Project not found for id " <> show pid)(mapMaybeT snapEval $ findUserProject uid pid)
t <- liftIO C.getCurrentTime(Just u, Just p, invCode) <- snapEval $(,,) <$> (runMaybeT $ findUser uid)<*> (runMaybeT $ findUserProject uid pid)<*> createInvitation pid uid toEmail tliftIO $ sendProjectInviteEmail cfg (p ^. projectName) (u ^. userEmail) toEmail invCode
t <- liftIO C.getCurrentTime(Just u, Just p, invCode) <-snapEval$ (,,)<$> (runMaybeT $ findUser uid)<*> (runMaybeT $ findUserProject uid pid)<*> createInvitation pid uid toEmail tliftIO $ sendProjectInviteEmail cfg(p ^. projectName)(u ^. userEmail)toEmailinvCode
sendProjectInviteEmail :: QConfig-> ProjectName-> Email -- Inviting user's email address-> Email -- Invitee's email address-> InvitationCode-> IO ()
sendProjectInviteEmail:: QConfig-> ProjectName-> Email -- Inviting user's email address-> Email -- Invitee's email address-> InvitationCode-> IO ()
let SmtpConfig{..} = cfg ^. QC.smtpConfigmailer = maybe (sendMailWithLogin _smtpHost) (sendMailWithLogin' _smtpHost) _smtpPortin buildProjectInviteEmail (cfg ^. templatePath) pn fromEmail toEmail invCode >>=(mailer _smtpUser _smtpPass)
let SmtpConfig {..} = cfg ^. QC.smtpConfigmailer = maybe (sendMailWithLogin _smtpHost)(sendMailWithLogin' _smtpHost)_smtpPortin buildProjectInviteEmail (cfg ^. templatePath) pn fromEmail toEmail invCode>>= (mailer _smtpUser _smtpPass)
buildProjectInviteEmail :: F.FilePath-> ProjectName-> Email -- Inviting user's email address-> Email -- Invitee's email address-> InvitationCode-> IO Mail
buildProjectInviteEmail:: F.FilePath-> ProjectName-> Email -- Inviting user's email address-> Email -- Invitee's email address-> InvitationCode-> IO Mail
let setAttrs = setAttribute "from_email" (fromEmail ^. _Email) .setAttribute "project_name" pn .setAttribute "to_email" (toEmail ^. _Email) .setAttribute "inv_code" (renderInvCode invCode)
let setAttrs =setAttribute "from_email" (fromEmail ^. _Email). setAttribute "project_name" pn. setAttribute "to_email" (toEmail ^. _Email). setAttribute "inv_code" (renderInvCode invCode)
import Data.Aeson as Aimport qualified Data.Map.Strict as Mimport Data.Text as Timport Data.Thyme.Clock as C
import Data.Aeson as Aimport qualified Data.Map.Strict as Mimport Data.Text as Timport Data.Thyme.Clock as C
let parseUser = User <$> (UserName <$> v .: "username")<*> (v .: "btcAddr")<*> (Email <$> v .: "email")
let parseUser =User<$> (UserName <$> v .: "username")<*> (v .: "btcAddr")<*> (Email <$> v .: "email")
userData <- maybe (snapError 400 "Could not parse user data") pure $ A.decode requestBodyt <- liftIO C.getCurrentTime
userData <- maybe (snapError 400 "Could not parse user data") pure$ A.decode requestBodyt <- liftIO C.getCurrentTime
let addr = stringToAddr (toNetwork nmode BTC) =<< (userData ^. cuser . userAddress)let createSUser = AU.createUser (userData ^. (cuser.username._UserName)) (userData ^. password)createQUser = snapEval $ douserId <- createUser ((userData ^. cuser) & userAddress .~ ((BTC,) <$> addr))void $ traverse (acceptInvitation userId t) (userData ^. invitationCodes)return userId
let addr =stringToAddr (toNetwork nmode BTC) =<< (userData ^. cuser . userAddress)letcreateSUser = AU.createUser (userData ^. (cuser . username . _UserName))(userData ^. password)createQUser = snapEval $ douserId <- createUser((userData ^. cuser) & userAddress .~ ((BTC, ) <$> addr))void $ traverse (acceptInvitation userId t) (userData ^. invitationCodes)return userId
uid <- requireUserIdt <- liftIO C.getCurrentTimeparams <- getParamsinvCodes <- maybe(snapError 400 "invCode parameter is required")(pure . traverse (parseInvCode . decodeUtf8))(M.lookup "invCode" params)
uid <- requireUserIdt <- liftIO C.getCurrentTimeparams <- getParamsinvCodes <- maybe (snapError 400 "invCode parameter is required")(pure . traverse (parseInvCode . decodeUtf8))(M.lookup "invCode" params)
import qualified Data.Text as Timport Data.Thyme.Clock as Cimport Data.UUID as Uimport Network.Haskoin.Address (Address, stringToAddr)
import qualified Data.Text as Timport Data.Thyme.Clock as Cimport Data.UUID as Uimport Network.Haskoin.Address ( Address, stringToAddr)
timestamp <- liftIO C.getCurrentTimecase A.eitherDecode requestBody >>= parseEither (parseLogEntry nmode uid evCtr) ofLeft err ->snapError 400 $ "Unable to parse log entry " <> (show requestBody) <> ": " <> show errRight entry ->snapEval $ createEvent pid uid (entry timestamp)
timestamp <- liftIO C.getCurrentTimecaseA.eitherDecode requestBody >>= parseEither (parseLogEntry nmode uid evCtr)ofLeft err ->snapError 400$ "Unable to parse log entry "<> (show requestBody)<> ": "<> show errRight entry -> snapEval $ createEvent pid uid (entry timestamp)
snapError 400 $ "Unable to parse bitcoin address from " <> (show addrBytes)Just addr ->snapEval . createEvent pid uid $LogEntry (CreditToCurrency (BTC, addr))(evCtr timestamp)(A.decode requestBody)
snapError 400$ "Unable to parse bitcoin address from "<> (show addrBytes)Just addr -> snapEval . createEvent pid uid $ LogEntry(CreditToCurrency (BTC, addr))(evCtr timestamp)(A.decode requestBody)
ival <- case endpoints of(Just s, Just e) -> pure $ During s e(Nothing, Just e) -> pure $ Before e(Just s, Nothing) -> pure $ After s(Nothing, Nothing) -> snapError 400 "You must at least one of the \"after\" or \"before\" query parameter"
ival <- case endpoints of(Just s , Just e ) -> pure $ During s e(Nothing, Just e ) -> pure $ Before e(Just s , Nothing) -> pure $ After s(Nothing, Nothing) -> snapError400"You must at least one of the \"after\" or \"before\" query parameter"
eventId <- maybe(snapError 400 "eventId parameter is required")(pure . EventId)(eventIdBytes >>= U.fromASCIIBytes)modTime <- ModTime <$> liftIO C.getCurrentTime
eventId <- maybe (snapError 400 "eventId parameter is required")(pure . EventId)(eventIdBytes >>= U.fromASCIIBytes)modTime <- ModTime <$> liftIO C.getCurrentTime
either(snapError 400 . T.pack)(snapEval . amendEvent uid eventId)(parseEither (parseEventAmendment nmode modTime) requestJSON)
either (snapError 400 . T.pack)(snapEval . amendEvent uid eventId)(parseEither (parseEventAmendment nmode modTime) requestJSON)
handleDBError (SubjectNotFound) =snapError 404 "The subject of the requested operation could not be found."
handleDBError (SubjectNotFound) = snapError404"The subject of the requested operation could not be found."
parseParam :: MonadSnap m=> Text -- ^ the name of the parameter to be parsed-> Parser a -- ^ parser for the value of the parameter-> m a -- ^ the parsed value
parseParam:: MonadSnap m=> Text -- ^ the name of the parameter to be parsed-> Parser a -- ^ parser for the value of the parameter-> m a -- ^ the parsed value
maybe (snapError 400 $ "Value of parameter \"" <> name <> "\" is not a valid UUID") pure maybeIdwhereidParser = dobs <- takeByteStringpure $ f <$> fromASCIIBytes bs
maybe(snapError 400 $ "Value of parameter \"" <> name <> "\" is not a valid UUID")puremaybeIdwhereidParser = dobs <- takeByteStringpure $ f <$> fromASCIIBytes bs
import Control.Lens ((^.), to)import Control.Exception (try)import qualified Data.Aeson as Aimport Data.ProtocolBuffers (encodeMessage)import Data.Serialize.Put (runPutLazy)import Filesystem.Path.CurrentOS (decodeString, encodeString)
import Control.Lens ( (^.), to)import Control.Exception ( try )import qualified Data.Aeson as Aimport Data.ProtocolBuffers ( encodeMessage )import Data.Serialize.Put ( runPutLazy )import Filesystem.Path.CurrentOS ( decodeString, encodeString)
cfg <- loadQConfig . decodeString $ fromRight "conf/aftok.cfg" cfgPathsconf <- snapConfig cfg
cfg <- loadQConfig . decodeString $ fromRight "conf/aftok.cfg" cfgPathsconf <- snapConfig cfg
sesss <- nestSnaplet "sessions" sess $initCookieSessionManager (cfg ^. authSiteKey . to encodeString)"quookie"(Just "aftok.com")(cfg ^. cookieTimeout)
sesss <- nestSnaplet "sessions" sess $ initCookieSessionManager(cfg ^. authSiteKey . to encodeString)"quookie"(Just "aftok.com")(cfg ^. cookieTimeout)
let nmode = cfg ^. billingConfig . C.networkModeloginRoute = method GET requireLogin >> redirect "/home"xhrLoginRoute = void $ method POST requireLoginregisterRoute = void $ method POST registerHandlerinviteRoute = void $ method POST (projectInviteHandler cfg)acceptInviteRoute = void $ method POST acceptInvitationHandlerprojectCreateRoute = serveJSON projectIdJSON $ method POST projectCreateHandlerprojectListRoute = serveJSON (fmap qdbProjectJSON) $ method GET projectListHandler
projectRoute = serveJSON projectJSON $ method GET projectGetHandlerlogEntriesRoute = serveJSON (fmap $ logEntryJSON nmode) $ method GET logEntriesHandlerlogIntervalsRoute = serveJSON (workIndexJSON nmode) $ method GET loggedIntervalsHandlerpayoutsRoute = serveJSON (payoutsJSON nmode) $ method GET payoutsHandler
letnmode = cfg ^. billingConfig . C.networkMode
logWorkRoute f = serveJSON eventIdJSON $ method POST (logWorkHandler f)logWorkBTCRoute f = serveJSON eventIdJSON $ method POST (logWorkBTCHandler f)amendEventRoute = serveJSON amendmentIdJSON $ method PUT amendEventHandler
loginRoute = method GET requireLogin >> redirect "/home"xhrLoginRoute = void $ method POST requireLoginregisterRoute = void $ method POST registerHandler
auctionCreateRoute = serveJSON auctionIdJSON $ method POST auctionCreateHandlerauctionRoute = serveJSON auctionJSON $ method GET auctionGetHandlerauctionBidRoute = serveJSON bidIdJSON $ method POST auctionBidHandler
inviteRoute = void $ method POST (projectInviteHandler cfg)acceptInviteRoute = void $ method POST acceptInvitationHandler
billableCreateRoute = serveJSON billableIdJSON $ method POST billableCreateHandlerbillableListRoute = serveJSON (fmap qdbBillableJSON) $ method GET billableListHandlersubscribeRoute = serveJSON subscriptionIdJSON $ method POST subscribeHandler
projectCreateRoute =serveJSON projectIdJSON $ method POST projectCreateHandlerprojectListRoute =serveJSON (fmap qdbProjectJSON) $ method GET projectListHandler
payableRequestsRoute = serveJSON billDetailsJSON $ method GET listPayableRequestsHandlergetPaymentRequestRoute = writeLBS . runPutLazy . encodeMessage =<< method GET getPaymentRequestHandlersubmitPaymentRoute = serveJSON paymentIdJSON $ method POST (paymentResponseHandler $ cfg ^. billingConfig)
projectRoute = serveJSON projectJSON $ method GET projectGetHandlerlogEntriesRoute =serveJSON (fmap $ logEntryJSON nmode) $ method GET logEntriesHandlerlogIntervalsRoute =serveJSON (workIndexJSON nmode) $ method GET loggedIntervalsHandler
, ("login", loginRoute), ("login", xhrLoginRoute), ("register", registerRoute), ("accept_invitation", acceptInviteRoute)
logWorkRoute f = serveJSON eventIdJSON $ method POST (logWorkHandler f)logWorkBTCRoute f =serveJSON eventIdJSON $ method POST (logWorkBTCHandler f)amendEventRoute = serveJSON amendmentIdJSON $ method PUT amendEventHandler
, ("projects/:projectId/logStart/:btcAddr", logWorkBTCRoute StartWork), ("projects/:projectId/logEnd/:btcAddr", logWorkBTCRoute StopWork), ("projects/:projectId/logStart", logWorkRoute StartWork), ("projects/:projectId/logEnd", logWorkRoute StopWork), ("projects/:projectId/logEntries", logEntriesRoute), ("projects/:projectId/intervals", logIntervalsRoute), ("projects/:projectId/auctions", auctionCreateRoute) -- <|> auctionListRoute, ("projects/:projectId/billables", billableCreateRoute <|> billableListRoute), ("projects/:projectId/payouts", payoutsRoute), ("projects/:projectId/invite", inviteRoute), ("projects/:projectId", projectRoute), ("projects", projectCreateRoute <|> projectListRoute)
auctionCreateRoute =serveJSON auctionIdJSON $ method POST auctionCreateHandlerauctionRoute = serveJSON auctionJSON $ method GET auctionGetHandlerauctionBidRoute = serveJSON bidIdJSON $ method POST auctionBidHandler
, ("auctions/:auctionId", auctionRoute), ("auctions/:auctionId/bid", auctionBidRoute)
billableCreateRoute =serveJSON billableIdJSON $ method POST billableCreateHandlerbillableListRoute =serveJSON (fmap qdbBillableJSON) $ method GET billableListHandlersubscribeRoute =serveJSON subscriptionIdJSON $ method POST subscribeHandler
, ("subscribe/:billableId", subscribeRoute), ("subscriptions/:subscriptionId/payment_requests", payableRequestsRoute), ("pay/:paymentRequestKey", getPaymentRequestRoute <|> submitPaymentRoute)
payableRequestsRoute =serveJSON billDetailsJSON $ method GET listPayableRequestsHandlergetPaymentRequestRoute =writeLBS. runPutLazy. encodeMessage=<< method GET getPaymentRequestHandlersubmitPaymentRoute = serveJSON paymentIdJSON$ method POST (paymentResponseHandler $ cfg ^. billingConfig)
, ("events/:eventId/amend", amendEventRoute)]
addRoutes[ ("static", serveDirectory . encodeString $ cfg ^. staticAssetPath), ("login" , loginRoute), ("login" , xhrLoginRoute), ("register" , registerRoute), ("accept_invitation" , acceptInviteRoute), ("projects/:projectId/logStart/:btcAddr", logWorkBTCRoute StartWork), ("projects/:projectId/logEnd/:btcAddr", logWorkBTCRoute StopWork), ("projects/:projectId/logStart" , logWorkRoute StartWork), ("projects/:projectId/logEnd" , logWorkRoute StopWork), ("projects/:projectId/logEntries" , logEntriesRoute), ("projects/:projectId/intervals" , logIntervalsRoute), ( "projects/:projectId/auctions", auctionCreateRoute) -- <|> auctionListRoute, ( "projects/:projectId/billables", billableCreateRoute <|> billableListRoute), ("projects/:projectId/payouts", payoutsRoute), ("projects/:projectId/invite" , inviteRoute), ("projects/:projectId" , projectRoute), ("projects" , projectCreateRoute <|> projectListRoute), ("auctions/:auctionId" , auctionRoute), ("auctions/:auctionId/bid" , auctionBidRoute), ("subscribe/:billableId" , subscribeRoute), ("subscriptions/:subscriptionId/payment_requests", payableRequestsRoute), ("pay/:paymentRequestKey", getPaymentRequestRoute <|> submitPaymentRoute), ("events/:eventId/amend" , amendEventRoute)]
genBid = Bid <$> (UserId <$> genUUID)<*> (Seconds <$> arbitrary `suchThat` (>= 0))<*> genSatoshi `suchThat` (> Satoshi 0)<*> arbitrary
genBid =Bid<$> (UserId <$> genUUID)<*> (Seconds <$> arbitrary `suchThat` (>= 0))<*> genSatoshi`suchThat` (> Satoshi 0)<*> arbitrary
let testB0 = Bid (UserId nil) (Seconds 3) (Satoshi 100) (read "2016-03-05 15:59:26.033176 UTC")testB1 = Bid (UserId nil) (Seconds 60) (Satoshi 1000)(read "2016-03-05 15:59:26.033177 UTC")testB2 = Bid (UserId nil) (Seconds 60) (Satoshi 100) (read "2016-03-05 15:59:26.033178 UTC")testB3 = Bid (UserId nil) (Seconds 90) (Satoshi 100) (read "2016-03-05 15:59:26.033179 UTC")testB4 = Bid (UserId nil) (Seconds 60) (Satoshi 100) (read "2016-03-05 15:59:26.033180 UTC")in dodescribe "bid ordering" $ doit "ensures that bids with lowest seconds/btc ratio are first" $ dobidOrder testB0 testB1 `shouldBe` LTbidOrder testB1 testB2 `shouldBe` LTbidOrder testB2 testB3 `shouldBe` LT
lettestB0 = Bid (UserId nil)(Seconds 3)(Satoshi 100)(read "2016-03-05 15:59:26.033176 UTC")testB1 = Bid (UserId nil)(Seconds 60)(Satoshi 1000)(read "2016-03-05 15:59:26.033177 UTC")testB2 = Bid (UserId nil)(Seconds 60)(Satoshi 100)(read "2016-03-05 15:59:26.033178 UTC")testB3 = Bid (UserId nil)(Seconds 90)(Satoshi 100)(read "2016-03-05 15:59:26.033179 UTC")testB4 = Bid (UserId nil)(Seconds 60)(Satoshi 100)(read "2016-03-05 15:59:26.033180 UTC")indodescribe "bid ordering" $ doit "ensures that bids with lowest seconds/btc ratio are first" $ dobidOrder testB0 testB1 `shouldBe` LTbidOrder testB1 testB2 `shouldBe` LTbidOrder testB2 testB3 `shouldBe` LT
describe "winning bids" $ doit "determines a sufficient number of winners to fulfill the raise amount" $let result = runAuction' (Satoshi 1250) [testB0, testB1, testB2, testB3, testB4]split = Bid (UserId nil) (Seconds 30) (Satoshi 50) (testB4 ^. bidTime)expected = sortBy bidOrder [testB0, testB1, testB2, split]in case result ofWinningBids winners ->sortBy bidOrder winners `shouldBe` expected
describe "winning bids" $ doit"determines a sufficient number of winners to fulfill the raise amount"$ letresult = runAuction' (Satoshi 1250)[testB0, testB1, testB2, testB3, testB4]split =Bid (UserId nil) (Seconds 30) (Satoshi 50) (testB4 ^. bidTime)expected = sortBy bidOrder [testB0, testB1, testB2, split]incase result ofWinningBids winners ->sortBy bidOrder winners `shouldBe` expected
InsufficientBids _ ->assertFailure "Sufficinent bids were presented, but auction algorithm asserted otherwise."
InsufficientBids _ ->assertFailure"Sufficinent bids were presented, but auction algorithm asserted otherwise."
it "ensures that the raise amount is fully consumed by the winning bids" $forAll ((,) <$> genSatoshi <*> listOf genBid) $\(raiseAmount', bids) ->case runAuction' raiseAmount' bids ofWinningBids xs -> bidsTotal xs == raiseAmount'InsufficientBids t -> t == (raiseAmount' - bidsTotal bids)
it "ensures that the raise amount is fully consumed by the winning bids"$ forAll ((,) <$> genSatoshi <*> listOf genBid)$ \(raiseAmount', bids) -> case runAuction' raiseAmount' bids ofWinningBids xs -> bidsTotal xs == raiseAmount'InsufficientBids t -> t == (raiseAmount' - bidsTotal bids)
startTime <- arbitraryintervals <- suchThat (buildIntervals startTime <$> deltas) (not.null)pure $ L.fromList intervals
startTime <- arbitraryintervals <- suchThat (buildIntervals startTime <$> deltas) (not . null)pure $ L.fromList intervals
it "reduces a log to a work index" $let testAddrs = catMaybes[ parseBtcAddr "123", parseBtcAddr "456", parseBtcAddr "789" ]
it "reduces a log to a work index"$ lettestAddrs = catMaybes[parseBtcAddr "123", parseBtcAddr "456", parseBtcAddr "789"]
in (workIndex testLogEntries) `shouldBe` expectedit "recovers a work index from events" $forAll genWorkIndex $ \(WorkIndex widx) ->let mergeAdjacent ((I.Interval s e) : (I.Interval s' e') : xs) | e == s' = mergeAdjacent $ I.Interval s e' : xs
it "recovers a work index from events"$ forAll genWorkIndex$ \(WorkIndex widx) ->letmergeAdjacent ((I.Interval s e) : (I.Interval s' e') : xs)| e == s' = mergeAdjacent $ I.Interval s e' : xs
ivalEntries addr ival = LogEntry addr <$> [StartWork (ival ^. start), StopWork (ival ^. end)]<*> [Nothing]
ivalEntries addr ival =LogEntry addr<$> [StartWork (ival ^. start), StopWork (ival ^. end)]<*> [Nothing]