on master help sign in
Got login working
a6dececiko
backend/Bridge.purs+1−1
@@ -15,6 +15,7 @@ import Prelude
15newtype Cmd_Login =
15newtype Cmd_Login =
16 Cmd_Login {
16 Cmd_Login {
17 email :: String
17 email :: String
18+ , password :: String
18 , server :: String
19 , server :: String
19 }
20 }
20
21
@@ -47,4 +48,3 @@ instance decodeJsonSub :: DecodeJson Sub where
47derive instance genericSub :: Generic Sub _
48derive instance genericSub :: Generic Sub _
48derive instance eqSub :: Eq Sub
49derive instance eqSub :: Eq Sub
49derive instance ordSub :: Ord Sub
50derive instance ordSub :: Ord Sub
50−
backend/Main.purs+11−8
@@ -3,27 +3,27 @@ module Main where
3import Prelude
3import Prelude
4
4
5import BW as WB
5import BW as WB
6+import BW.Logic as Logic
7+import BW.Types (Email(..), Password(..))
6import Bridge (Sub(..))
8import Bridge (Sub(..))
7import Bridge as Bridge
9import Bridge as Bridge
8−import Control.Promise (toAff)
10+import Control.Monad.Reader (runReaderT)
9−import Data.Argonaut as A
10import Data.Either (Either(..))
11import Data.Either (Either(..))
11import Data.Maybe (Maybe(..))
12import Data.Maybe (Maybe(..))
12import Data.Nullable (null, toNullable)
13import Data.Nullable (null, toNullable)
13import Effect (Effect)
14import Effect (Effect)
14−import Effect.Aff (joinFiber, runAff, runAff_)
15+import Effect.Aff (runAff_)
15import Effect.Console (log)
16import Effect.Console (log)
16import Elm as Elm
17import Elm as Elm
17import FFI as FFI
18import FFI as FFI
18−import Foreign as F
19
19
20
20
21main :: Effect Unit
21main :: Effect Unit
22main = do
22main = do
23 app <- FFI.startElm "elm"
23 app <- FFI.startElm "elm"
24− Elm.subscribe app \(Bridge.Login (Bridge.Cmd_Login {email, server})) -> do
24+ Elm.subscribe app \(Bridge.Login (Bridge.Cmd_Login {email, server, password})) -> do
25 log $ "Got cmd"
25 log $ "Got cmd"
26− bwAPI <- WB.getAPI
26+ services <- WB.getServices
27 { base : toNullable $ Just server,
27 { base : toNullable $ Just server,
28 webVault : null,
28 webVault : null,
29 api : null,
29 api : null,
@@ -34,9 +34,12 @@ main = do
34 keyConnector : null
34 keyConnector : null
35 }
35 }
36 runAff_ (\res -> case res of
36 runAff_ (\res -> case res of
37− Left _ -> log "Error"
37+ Left e -> log $ show e
38 Right x -> log $ show x
38 Right x -> log $ show x
39− ) $ toAff $ bwAPI.postPrelogin {email}
39+ ) $ runReaderT (Logic.getLogInRequestToken (Email email) (Password password)) {
40+ api: services.api,
41+ crypto : services.crypto
42+ }
40 pure unit
43 pure unit
41
44
42 Elm.send app Hello
45 Elm.send app Hello
bridge.yaml+1
@@ -5,3 +5,4 @@ Cmd:
5- Login:
5- Login:
6 email: String
6 email: String
7 server: String
7 server: String
8+ password: String
bw/spago.dhall+2−1
@@ -10,7 +10,8 @@
10 , "maybe"
10 , "maybe"
11 , "newtype"
11 , "newtype"
12 , "prelude"
12 , "prelude"
13− , "ell"
13+ , "el"
14+ , "arraybuffer-types"
14 ]
15 ]
15, packages = ../packages.dhall
16, packages = ../packages.dhall
16, sources = [ "src/**/*.purs" ]
17, sources = [ "src/**/*.purs" ]
bw/src/BW.js+21−3
@@ -10,17 +10,35 @@ import { WebCryptoFunctionService } from "../../deps/bw/libs/shared/dist/src/ser
10import { Account } from "../../deps/bw/libs/shared/dist/src/models/domain/account";
10import { Account } from "../../deps/bw/libs/shared/dist/src/models/domain/account";
11
11
12// import BrowserStorageService from "../../deps/bw/libs/shared/dist/src/services/browserStorage.service";
12// import BrowserStorageService from "../../deps/bw/libs/shared/dist/src/services/browserStorage.service";
13−import { CryptoService } from "../../deps/bw/libs/shared/dist/src/abstractions/crypto.service";
13+import { CryptoService } from "../../deps/bw/libs/shared/dist/src/services/crypto.service";
14+
14import { I18nService } from "../../deps/bw/libs/shared/dist/src/services/i18n.service";
15import { I18nService } from "../../deps/bw/libs/shared/dist/src/services/i18n.service";
15import { StateService } from "../../deps/bw/libs/shared/dist/src/services/state.service";
16import { StateService } from "../../deps/bw/libs/shared/dist/src/services/state.service";
17+import { PasswordTokenRequest } from "../../deps/bw/libs/shared/dist/src/models/request/identityToken/passwordTokenRequest";
18+import { TokenRequestTwoFactor } from "../../deps/bw/libs/common/src/models/request/identityToken/tokenRequestTwoFactor.ts";
16
19
17−export function getAPI(urls) {
20+export function getServices(urls) {
18 return function () {
21 return function () {
19 const bg = new MainBackground()
22 const bg = new MainBackground()
20
23
21 bg.environmentService.setUrls(urls)
24 bg.environmentService.setUrls(urls)
22
25
23− return bg.apiService
26+ return {
27+ api: {
28+ postPrelogin: (req) => bg.apiService.postPrelogin(req),
29+ getProfile: (req) => bg.apiService.getProfile(req),
30+ postIdentityToken: (req) => bg.apiService.postIdentityToken(
31+ new PasswordTokenRequest(
32+ email = req.email,
33+ masterPasswordHash = req.masterPasswordHash,
34+ captchaResponse = req.captchaResponse,
35+ twoFactor = new TokenRequestTwoFactor(req.twoFactor),
36+ device = req.device
37+ )
38+ )
39+ },
40+ crypto: bg.cryptoService
41+ }
24 }
42 }
25}
43}
26
44
bw/src/BW.purs+17−4
@@ -1,16 +1,29 @@
1module BW where
1module BW where
2
2
3+import BW.Types
3import Prelude
4import Prelude
4
5
5−import BW.Types
6import Control.Promise (Promise)
6import Control.Promise (Promise)
7+import Data.Function.Uncurried (Fn3, Fn4)
8+import Data.Nullable (Nullable)
7import Effect (Effect)
9import Effect (Effect)
8
10
9−
11+type CryptoService = {
12+ makeKey :: Fn4 Password String KDF Int (Promise SymmetricCryptoKey),
13+ hashPassword :: Fn3 Password SymmetricCryptoKey (Nullable HashPurpose) (Promise Hash)
14+}
10
15
11type ApiService = {
16type ApiService = {
12 postPrelogin :: PreloginRequest -> Promise PreloginResponse,
17 postPrelogin :: PreloginRequest -> Promise PreloginResponse,
13− getProfile :: Unit -> Promise ProfileResponse
18+ getProfile :: Unit -> Promise ProfileResponse,
19+
20+ -- TODO: There can really be more request and response types. Maybe handle this later.
21+ postIdentityToken :: PasswordTokenRequest -> Promise IdentityTokenResponse
22+}
23+
24+type Services = {
25+ crypto :: CryptoService,
26+ api :: ApiService
14}
27}
15
28
16−foreign import getAPI :: Urls -> Effect ApiService
29+foreign import getServices :: Urls -> Effect Services
bw/src/BW/Logic.purs+47−15
@@ -1,27 +1,59 @@
1module BW.Logic where
1module BW.Logic where
2
2
3+import BW
3import BW.Types
4import BW.Types
5+import Control.El
4import Prelude
6import Prelude
5
7
6−import Control.Ell (class EllHas, Ell, l)
8+import Control.Promise as Promise
9+import Data.Function.Uncurried (runFn3, runFn4)
10+import Data.Maybe (Maybe(..))
7−import Data.Nullable (null)
11+import Data.Nullable (null, toNullable)
12+import Data.String as String
13+import Effect.Aff.Class (liftAff)
8
14
9−newtype Email = Email String
15+makePreloginKey ::
16+ forall r.
17+ HasL "api" ApiService r =>
18+ HasL "crypto" CryptoService r =>
19+ Email -> Password -> Al r SymmetricCryptoKey
20+makePreloginKey (Email email') (Password password) = do
21+ api :: ApiService <- l (L :: L "api")
22+ crypto <- l (L :: L "crypto")
10
23
11−newtype Password = Password String
24+ let email = (String.trim >>> String.toLower) email'
12
25
13−generateLogin :: forall r. EllHas (hashPassword :: String -> Ell r String) r => Email -> Password -> Ell r PasswordTokenRequest
26+ {kdf, kdfIterations} <- liftAff $ Promise.toAff $ api.postPrelogin {email}
14−generateLogin (Email email) (Password password) = do
27+ liftAff $ Promise.toAff $ runFn4 crypto.makeKey (Password password) email kdf kdfIterations
15− _ <- l _.hashPassword ""
28+
16− -- let token = user ++ password
29+getLogInRequestToken ::
17− pure $
30+ forall r.
31+ HasL "api" ApiService r =>
32+ HasL "crypto" CryptoService r =>
33+ Email -> Password -> Al r IdentityTokenResponse
34+getLogInRequestToken email password = do
35+ key <- makePreloginKey email password
36+ crypto <- l (L :: L "crypto")
37+ api :: ApiService <- l (L :: L "api")
38+
39+ _localHashedPassword <- liftAff $ Promise.toAff $
40+ runFn3 crypto.hashPassword password key (toNullable $ Just hashPurposeLocalAuthorization)
41+
42+ Hash hashedPassword <- liftAff $ Promise.toAff $ runFn3 crypto.hashPassword password key null
43+
44+ liftAff $ Promise.toAff $ api.postIdentityToken {
18− { email
45+ email: email,
19− , masterPasswordHash: ""
46+ masterPasswordHash: hashedPassword,
20− , captchaResponse: ""
47+ captchaResponse: "",
21− , twoFactor: {
48+ twoFactor: {
22 provider: twoFactorProviderTypeEmail,
49 provider: twoFactorProviderTypeEmail,
23 token: "",
50 token: "",
24− remember: true
51+ remember: false
52+ },
53+ device: {
54+ type: deviceTypeUnknownBrowser,
55+ name: "Temporary device name",
56+ identifier: "42",
57+ pushToken: null
25 }
58 }
26− , device: null
27 }
59 }
bw/src/BW/Types.purs+79−2
@@ -2,6 +2,7 @@ module BW.Types where
2
2
3import Prelude
3import Prelude
4
4
5+import Data.ArrayBuffer.Types (ArrayBuffer)
5import Data.Date (Date)
6import Data.Date (Date)
6import Data.List (List)
7import Data.List (List)
7import Data.Nullable (Nullable)
8import Data.Nullable (Nullable)
@@ -188,13 +189,43 @@ type Urls = {
188}
189}
189
190
190type PasswordTokenRequest = {
191type PasswordTokenRequest = {
191− email :: String,
192+ email :: Email,
192 masterPasswordHash :: String,
193 masterPasswordHash :: String,
193 captchaResponse :: String,
194 captchaResponse :: String,
194 twoFactor :: TokenRequestTwoFactor,
195 twoFactor :: TokenRequestTwoFactor,
195− device :: Nullable Unit -- we won't use this, so leave this null
196+ device :: DeviceRequest
197+}
198+
199+type DeviceRequest = {
200+ type :: DeviceType,
201+ name :: String,
202+ identifier :: String,
203+ pushToken :: Nullable String
196}
204}
197
205
206+deviceTypeAndroid = 0 :: DeviceType
207+deviceTypeiOS = 1 :: DeviceType
208+deviceTypeChromeExtension = 2 :: DeviceType
209+deviceTypeFirefoxExtension = 3 :: DeviceType
210+deviceTypeOperaExtension = 4 :: DeviceType
211+deviceTypeEdgeExtension = 5 :: DeviceType
212+deviceTypeWindowsDesktop = 6 :: DeviceType
213+deviceTypeMacOsDesktop = 7 :: DeviceType
214+deviceTypeLinuxDesktop = 8 :: DeviceType
215+deviceTypeChromeBrowser = 9 :: DeviceType
216+deviceTypeFirefoxBrowser = 10 :: DeviceType
217+deviceTypeOperaBrowser = 11 :: DeviceType
218+deviceTypeEdgeBrowser = 12 :: DeviceType
219+deviceTypeIEBrowser = 13 :: DeviceType
220+deviceTypeUnknownBrowser = 14 :: DeviceType
221+deviceTypeAndroidAmazon = 15 :: DeviceType
222+deviceTypeUWP = 16 :: DeviceType
223+deviceTypeSafariBrowser = 17 :: DeviceType
224+deviceTypeVivaldiBrowser = 18 :: DeviceType
225+deviceTypeVivaldiExtension = 19 :: DeviceType
226+deviceTypeSafariExtension = 20 :: DeviceType
227+type DeviceType = Int
228+
198type TokenRequestTwoFactor = {
229type TokenRequestTwoFactor = {
199 provider :: TwoFactorProviderType,
230 provider :: TwoFactorProviderType,
200 token :: String,
231 token :: String,
@@ -210,3 +241,49 @@ twoFactorProviderTypeRemember = 5 :: TwoFactorProviderType
210twoFactorProviderTypeOrganizationDuo = 6 :: TwoFactorProviderType
241twoFactorProviderTypeOrganizationDuo = 6 :: TwoFactorProviderType
211twoFactorProviderTypeWebAuthn = 7 :: TwoFactorProviderType
242twoFactorProviderTypeWebAuthn = 7 :: TwoFactorProviderType
212type TwoFactorProviderType = Int
243type TwoFactorProviderType = Int
244+
245+
246+type SymmetricCryptoKey = {
247+ key :: ArrayBuffer,
248+ encKey :: Nullable ArrayBuffer,
249+ macKey :: Nullable ArrayBuffer,
250+ encType :: EncryptionType,
251+ keyB64 :: String,
252+ encKeyB64 :: String,
253+ macKeyB64 :: String
254+}
255+
256+encryptionTypeAesCbc256_B64 = 0 :: EncryptionType
257+encryptionTypeAesCbc128_HmacSha256_B64 = 1 :: EncryptionType
258+encryptionTypeAesCbc256_HmacSha256_B64 = 2 :: EncryptionType
259+encryptionTypeRsa2048_OaepSha256_B64 = 3 :: EncryptionType
260+encryptionTypeRsa2048_OaepSha1_B64 = 4 :: EncryptionType
261+encryptionTypeRsa2048_OaepSha256_HmacSha256_B64 = 5 :: EncryptionType
262+encryptionTypeRsa2048_OaepSha1_HmacSha256_B64 = 6 :: EncryptionType
263+type EncryptionType = Int
264+
265+newtype Password = Password String
266+
267+hashPurposeServerAuthorization = 1 :: HashPurpose
268+hashPurposeLocalAuthorization = 2 :: HashPurpose
269+type HashPurpose = Int
270+
271+newtype Hash = Hash String
272+
273+type IdentityTokenResponse = {
274+ accessToken :: String,
275+ expiresIn :: Int,
276+ refreshToken :: String,
277+ tokenType :: String,
278+ resetMasterPassword :: Boolean,
279+ privateKey :: String,
280+ key :: String,
281+ twoFactorToken :: Nullable String,
282+ kdf :: KDF,
283+ kdfIterations :: Int,
284+ forcePasswordReset :: Nullable Boolean,
285+ apiUseKeyConnector :: Nullable Boolean,
286+ keyConnectorUrl :: Nullable String
287+}
288+
289+newtype Email = Email String
el/spago.dhallnew file+14
@@ -0,0 +1,14 @@
1+{ name = "bw"
2+, dependencies =
3+ [ "aff"
4+ , "effect"
5+ , "newtype"
6+ , "prelude"
7+ , "record"
8+ , "test-unit"
9+ , "transformers"
10+ , "unsafe-coerce"
11+ ]
12+, packages = ../packages.dhall
13+, sources = [ "src/**/*.purs", "test/**/*.purs" ]
14+}
el/src/Control/El.pursnew file+23
@@ -0,0 +1,23 @@
1+module Control.El (El, Al, l, class HasL, L(..)) where
2+
3+
4+import Control.Monad.Reader (class MonadAsk, ReaderT, asks)
5+import Data.Symbol (class IsSymbol, reflectSymbol)
6+import Effect (Effect)
7+import Effect.Aff (Aff)
8+import Prim.Row (class Cons, class Nub)
9+import Record.Unsafe (unsafeGet)
10+import Type.Proxy (Proxy(..))
11+
12+data L :: forall k. k -> Type
13+data L t = L
14+
15+type El r = ReaderT (Record r) Effect
16+type Al r = ReaderT (Record r) Aff
17+
18+l :: forall res r field m. MonadAsk (Record r) m => HasL field res r => IsSymbol field => L field -> m res
19+l L = asks (unsafeGet (reflectSymbol (Proxy :: Proxy field)))
20+
21+class HasL :: forall k1 k2 k3. k1 -> k2 -> k3 -> Constraint
22+class HasL field t r | field r -> t
23+instance (Cons field t r r', Nub r' r) => HasL field t r
el/test/Main.pursnew file+27
@@ -0,0 +1,27 @@
1+module Test.Main
2+ ( main
3+ )
4+ where
5+
6+import Prelude
7+import Test.Unit
8+
9+import Control.El
10+import Control.Monad.Reader (runReaderT)
11+import Effect (Effect)
12+import Effect.Aff (launchAff_)
13+import Test.Unit.Assert as Assert
14+import Test.Unit.Output.Fancy (runTest)
15+import Type.Proxy (Proxy(..))
16+
17+main ∷ Effect Unit
18+main = launchAff_ $ void $ runTest do
19+ suite "sync code" do
20+ test "arithmetic" do
21+ x <- runReaderT singleHandler {handle: 2, ignore: "Hello"}
22+ Assert.equal x 2
23+
24+
25+singleHandler :: forall r. HasL "handle" Int r => Al r Int
26+singleHandler = do
27+ l (L :: L "handle")
ell/spago.dhalldeleted−8
@@ -1,8 +0,0 @@
1−{ name = "bw"
2−, dependencies =
3− [ "prelude"
4− , "transformers"
5− ]
6−, packages = ../packages.dhall
7−, sources = [ "src/**/*.purs" ]
8−}
ell/src/Control/Ell.pursdeleted−31
@@ -1,31 +0,0 @@
1−module Control.Ell where
2−
3−import Prelude
4−
5−import Control.Monad.Reader (ReaderT, asks)
6−import Effect (Effect)
7−import Prim.Row (class Cons, class Union, class Nub)
8−
9−type Ell r a = ReaderT { | r } Effect a
10−
11−l :: forall r a res. ApplyEll r a res => ({ | r } -> a) -> res
12−l f = appEll (asks f)
13−
14−class EllHas row r
15−
16−instance (Union row r r', Nub r' r) => EllHas row r
17−
18−class ApplyEll r a res | r a -> res where
19− appEll :: Ell r a -> res
20−
21−instance ApplyEll r b c => ApplyEll r (a -> b) (a -> c) where
22− appEll f a = appEll ((\g -> g a) <$> f)
23−
24−else instance ApplyEll r (Ell r a) (Ell r a) where
25− appEll = join
26−
27−else instance ApplyEll r a (Ell r a) where
28− appEll = identity
29−
30−foo ∷ ∀ (t55 ∷ Type) (t56 ∷ Row Type). { foo ∷ t55 | t56 } → t55
31−foo = _.foo
elm.js+32−3
@@ -10590,7 +10590,7 @@ var $author$project$FFI$getBridge = F2(
10590 }
10590 }
10591 }));
10591 }));
10592 });
10592 });
10593−var $author$project$Main$init = {email: '', error: $elm$core$Maybe$Nothing, messages: _List_Nil, server: ''};
10593+var $author$project$Main$init = {email: '', error: $elm$core$Maybe$Nothing, messages: _List_Nil, password: '', server: ''};
10594var $author$project$Bridge$Login = function (a) {
10594var $author$project$Bridge$Login = function (a) {
10595 return {$: 'Login', a: a};
10595 return {$: 'Login', a: a};
10596};
10596};
@@ -10603,6 +10603,9 @@ var $author$project$Bridge$jsonEncCmd_Login = function (val) {
10603 'email',
10603 'email',
10604 $elm$json$Json$Encode$string(val.email)),
10604 $elm$json$Json$Encode$string(val.email)),
10605 _Utils_Tuple2(
10605 _Utils_Tuple2(
10606+ 'password',
10607+ $elm$json$Json$Encode$string(val.password)),
10608+ _Utils_Tuple2(
10606 'server',
10609 'server',
10607 $elm$json$Json$Encode$string(val.server))
10610 $elm$json$Json$Encode$string(val.server))
10608 ]));
10611 ]));
@@ -10650,18 +10653,28 @@ var $author$project$Main$update = F2(
10650 model,
10653 model,
10651 {server: server}),
10654 {server: server}),
10652 $elm$core$Platform$Cmd$none);
10655 $elm$core$Platform$Cmd$none);
10656+ case 'UpdatePassword':
10657+ var password = msg.a;
10658+ return _Utils_Tuple2(
10659+ _Utils_update(
10660+ model,
10661+ {password: password}),
10662+ $elm$core$Platform$Cmd$none);
10653 default:
10663 default:
10654 return _Utils_Tuple2(
10664 return _Utils_Tuple2(
10655 model,
10665 model,
10656 $author$project$FFI$sendBridge(
10666 $author$project$FFI$sendBridge(
10657 $author$project$Bridge$Login(
10667 $author$project$Bridge$Login(
10658− {email: model.email, server: model.server})));
10668+ {email: model.email, password: model.password, server: model.server})));
10659 }
10669 }
10660 });
10670 });
10661var $author$project$Main$Submit = {$: 'Submit'};
10671var $author$project$Main$Submit = {$: 'Submit'};
10662var $author$project$Main$UpdateEmail = function (a) {
10672var $author$project$Main$UpdateEmail = function (a) {
10663 return {$: 'UpdateEmail', a: a};
10673 return {$: 'UpdateEmail', a: a};
10664};
10674};
10675+var $author$project$Main$UpdatePassword = function (a) {
10676+ return {$: 'UpdatePassword', a: a};
10677+};
10665var $author$project$Main$UpdateServer = function (a) {
10678var $author$project$Main$UpdateServer = function (a) {
10666 return {$: 'UpdateServer', a: a};
10679 return {$: 'UpdateServer', a: a};
10667};
10680};
@@ -10765,6 +10778,22 @@ var $author$project$Main$view = function (model) {
10765 A2(
10778 A2(
10766 $elm$html$Html$h4,
10779 $elm$html$Html$h4,
10767 _List_Nil,
10780 _List_Nil,
10781+ _List_fromArray(
10782+ [
10783+ $elm$html$Html$text('Password')
10784+ ])),
10785+ A2(
10786+ $elm$html$Html$input,
10787+ _List_fromArray(
10788+ [
10789+ $elm$html$Html$Attributes$type_('text'),
10790+ $elm$html$Html$Attributes$value(model.password),
10791+ $elm$html$Html$Events$onInput($author$project$Main$UpdatePassword)
10792+ ]),
10793+ _List_Nil),
10794+ A2(
10795+ $elm$html$Html$h4,
10796+ _List_Nil,
10768 _List_fromArray(
10797 _List_fromArray(
10769 [
10798 [
10770 $elm$html$Html$text('Server')
10799 $elm$html$Html$text('Server')
@@ -10831,4 +10860,4 @@ var $author$project$Main$main = $elm$browser$Browser$element(
10831 view: $author$project$Main$view
10860 view: $author$project$Main$view
10832 });
10861 });
10833_Platform_export({'Main':{'init':$author$project$Main$main(
10862_Platform_export({'Main':{'init':$author$project$Main$main(
10834− $elm$json$Json$Decode$succeed(_Utils_Tuple0))({"versions":{"elm":"0.19.1"},"types":{"message":"Main.Msg","aliases":{},"unions":{"Main.Msg":{"args":[],"tags":{"RecieveMessage":["String.String"],"Error":["String.String"],"UpdateEmail":["String.String"],"UpdateServer":["String.String"],"Submit":[]}},"String.String":{"args":[],"tags":{"String":[]}}}}})}});}(this));
10863+ $elm$json$Json$Decode$succeed(_Utils_Tuple0))({"versions":{"elm":"0.19.1"},"types":{"message":"Main.Msg","aliases":{},"unions":{"Main.Msg":{"args":[],"tags":{"RecieveMessage":["String.String"],"Error":["String.String"],"UpdateEmail":["String.String"],"UpdateServer":["String.String"],"UpdatePassword":["String.String"],"Submit":[]}},"String.String":{"args":[],"tags":{"String":[]}}}}})}});}(this));
frontend/Bridge.elm+4−1
@@ -10,19 +10,22 @@ import Set exposing (Set)
10
10
11type alias Cmd_Login =
11type alias Cmd_Login =
12 { email: String
12 { email: String
13+ , password: String
13 , server: String
14 , server: String
14 }
15 }
15
16
16jsonDecCmd_Login : Json.Decode.Decoder ( Cmd_Login )
17jsonDecCmd_Login : Json.Decode.Decoder ( Cmd_Login )
17jsonDecCmd_Login =
18jsonDecCmd_Login =
18− Json.Decode.succeed (\pemail pserver -> {email = pemail, server = pserver})
19+ Json.Decode.succeed (\pemail ppassword pserver -> {email = pemail, password = ppassword, server = pserver})
19 |> required "email" (Json.Decode.string)
20 |> required "email" (Json.Decode.string)
21+ |> required "password" (Json.Decode.string)
20 |> required "server" (Json.Decode.string)
22 |> required "server" (Json.Decode.string)
21
23
22jsonEncCmd_Login : Cmd_Login -> Value
24jsonEncCmd_Login : Cmd_Login -> Value
23jsonEncCmd_Login val =
25jsonEncCmd_Login val =
24 Json.Encode.object
26 Json.Encode.object
25 [ ("email", Json.Encode.string val.email)
27 [ ("email", Json.Encode.string val.email)
28+ , ("password", Json.Encode.string val.password)
26 , ("server", Json.Encode.string val.server)
29 , ("server", Json.Encode.string val.server)
27 ]
30 ]
28
31
frontend/Main.elm+17−1
@@ -14,6 +14,7 @@ type Msg
14 | Error String
14 | Error String
15 | UpdateEmail String
15 | UpdateEmail String
16 | UpdateServer String
16 | UpdateServer String
17+ | UpdatePassword String
17 | Submit
18 | Submit
18
19
19
20
@@ -22,6 +23,7 @@ type alias Model =
22 , error : Maybe String
23 , error : Maybe String
23 , server : String
24 , server : String
24 , email : String
25 , email : String
26+ , password : String
25 }
27 }
26
28
27
29
@@ -41,6 +43,7 @@ init =
41 , error = Nothing
43 , error = Nothing
42 , server = ""
44 , server = ""
43 , email = ""
45 , email = ""
46+ , password = ""
44 }
47 }
45
48
46
49
@@ -52,6 +55,8 @@ view model =
52 , ul [] (model.messages |> List.map (text >> List.singleton >> li []))
55 , ul [] (model.messages |> List.map (text >> List.singleton >> li []))
53 , h4 [] [ text "Email" ]
56 , h4 [] [ text "Email" ]
54 , input [ Attr.type_ "text", Attr.value model.email, Ev.onInput UpdateEmail ] []
57 , input [ Attr.type_ "text", Attr.value model.email, Ev.onInput UpdateEmail ] []
58+ , h4 [] [ text "Password" ]
59+ , input [ Attr.type_ "text", Attr.value model.password, Ev.onInput UpdatePassword ] []
55 , h4 [] [ text "Server" ]
60 , h4 [] [ text "Server" ]
56 , input [ Attr.type_ "text", Attr.value model.server, Ev.onInput UpdateServer ] []
61 , input [ Attr.type_ "text", Attr.value model.server, Ev.onInput UpdateServer ] []
57 , button [ Ev.onClick Submit ] [ text "Submit" ]
62 , button [ Ev.onClick Submit ] [ text "Submit" ]
@@ -80,5 +85,16 @@ update msg model =
80 UpdateServer server ->
85 UpdateServer server ->
81 ( { model | server = server }, Cmd.none )
86 ( { model | server = server }, Cmd.none )
82
87
88+ UpdatePassword password ->
89+ ( { model | password = password }, Cmd.none )
90+
83 Submit ->
91 Submit ->
92+ ( model
93+ , FFI.sendBridge
94+ (Bridge.Login
95+ { email = model.email
84− ( model, FFI.sendBridge (Bridge.Login { email = model.email, server = model.server }) )
96+ , server = model.server
97+ , password = model.password
98+ }
99+ )
100+ )
packages.dhall+26−25
@@ -103,28 +103,29 @@ let upstream =
103 sha256:7a24ebdbacb2bfa27b2fc6ce3da96f048093d64e54369965a2a7b5d9892b6031
103 sha256:7a24ebdbacb2bfa27b2fc6ce3da96f048093d64e54369965a2a7b5d9892b6031
104
104
105in upstream
105in upstream
106− with bw = ./bw/spago.dhall as Location
106+ with bw = ./bw/spago.dhall as Location
107− with ell = ./ell/spago.dhall as Location
107+ with el = ./el/spago.dhall as Location
108− with argonaut-aeson-generic =
108+ with argonaut-aeson-generic =
109− { dependencies =
109+ { dependencies =
110− [ "argonaut-codecs"
110+ [ "argonaut-codecs"
111− , "argonaut-core"
111+ , "argonaut-core"
112− , "argonaut-generic"
112+ , "argonaut-generic"
113− , "arrays"
113+ , "arrays"
114− , "bifunctors"
114+ , "bifunctors"
115− , "control"
115+ , "control"
116− , "effect"
116+ , "effect"
117− , "either"
117+ , "either"
118− , "foldable-traversable"
118+ , "foldable-traversable"
119− , "foreign-object"
119+ , "foreign-object"
120− , "maybe"
120+ , "maybe"
121− , "partial"
121+ , "partial"
122− , "prelude"
122+ , "prelude"
123− , "psci-support"
123+ , "psci-support"
124− , "record"
124+ , "record"
125− , "test-unit"
125+ , "test-unit"
126− , "typelevel-prelude"
126+ , "typelevel-prelude"
127− ]
127+ ]
128+ , repo =
128− , repo = "https://github.com/ilyakooo0/purescript-argonaut-aeson-generic.git"
129+ "https://github.com/ilyakooo0/purescript-argonaut-aeson-generic.git"
129− , version = "purescript-0.14"
130+ , version = "purescript-0.14"
130− }
131+ }
spago.dhall+1
@@ -26,6 +26,7 @@ to generate this file without the comments in this block.
26 , "newtype"
26 , "newtype"
27 , "nullable"
27 , "nullable"
28 , "prelude"
28 , "prelude"
29+ , "transformers"
29 ]
30 ]
30, packages = ./packages.dhall
31, packages = ./packages.dhall
31, sources = [ "backend/**/*.purs" ]
32, sources = [ "backend/**/*.purs" ]