elm-pages 3.0.0-beta.1 → 3.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/codegen/elm-pages-codegen.js +803 -284
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +285 -101
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
- package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +140 -17
- package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +218 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
- package/generator/src/SharedTemplate.elm +1 -1
- package/generator/src/build.js +75 -42
- package/generator/src/compatibility-key.js +1 -0
- package/generator/src/config.js +41 -0
- package/generator/src/dev-server.js +36 -56
- package/generator/src/generate-template-module-connector.js +0 -28
- package/generator/src/pre-render-html.js +31 -17
- package/generator/src/render.js +2 -0
- package/generator/src/seo-renderer.js +11 -4
- package/generator/src/vite-utils.js +78 -0
- package/generator/template/app/Api.elm +1 -1
- package/generator/template/app/Site.elm +6 -1
- package/package.json +2 -3
- package/src/ApiRoute.elm +0 -3
- package/src/DataSource/File.elm +1 -1
- package/src/DataSource/Internal/Request.elm +0 -5
- package/src/DataSource.elm +39 -31
- package/src/Form/Field.elm +1 -1
- package/src/Form.elm +1 -1
- package/src/Head/Seo.elm +16 -27
- package/src/Head.elm +126 -0
- package/src/HtmlPrinter.elm +7 -3
- package/src/Pages/Generate.elm +544 -102
- package/src/Pages/Internal/NotFoundReason.elm +3 -2
- package/src/Pages/Internal/Platform/Cli.elm +91 -27
- package/src/Pages/Internal/Platform/Cli.elm.bak +1276 -0
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +6 -0
- package/src/Pages/Internal/Platform.elm +34 -27
- package/src/Pages/ProgramConfig.elm +6 -3
- package/src/Server/Session.elm +149 -83
- package/src/Server/SetCookie.elm +89 -31
|
@@ -58,7 +58,7 @@ type alias Program userModel userMsg pageData actionData sharedData errorPage =
|
|
|
58
58
|
mainView :
|
|
59
59
|
ProgramConfig userMsg userModel route pageData actionData sharedData effect (Msg userMsg pageData actionData sharedData errorPage) errorPage
|
|
60
60
|
-> Model userModel pageData actionData sharedData
|
|
61
|
-
-> { title : String, body : Html (Pages.Msg.Msg userMsg) }
|
|
61
|
+
-> { title : String, body : List (Html (Pages.Msg.Msg userMsg)) }
|
|
62
62
|
mainView config model =
|
|
63
63
|
case model.notFound of
|
|
64
64
|
Just info ->
|
|
@@ -95,7 +95,7 @@ mainView config model =
|
|
|
95
95
|
Err error ->
|
|
96
96
|
{ title = "Page Data Error"
|
|
97
97
|
, body =
|
|
98
|
-
Html.div [] [ Html.text error ]
|
|
98
|
+
[ Html.div [] [ Html.text error ] ]
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
|
|
@@ -124,9 +124,9 @@ view config model =
|
|
|
124
124
|
{ title = title
|
|
125
125
|
, body =
|
|
126
126
|
[ onViewChangeElement model.url
|
|
127
|
-
, body |> Html.map UserMsg
|
|
128
127
|
, AriaLiveAnnouncer.view model.ariaNavigationAnnouncement
|
|
129
128
|
]
|
|
129
|
+
++ List.map (Html.map UserMsg) body
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
|
|
@@ -423,9 +423,7 @@ update config appMsg model =
|
|
|
423
423
|
)
|
|
424
424
|
|
|
425
425
|
else
|
|
426
|
-
(
|
|
427
|
-
| url = url
|
|
428
|
-
}
|
|
426
|
+
( model
|
|
429
427
|
, NoEffect
|
|
430
428
|
)
|
|
431
429
|
-- TODO is it reasonable to always re-fetch route data if you re-navigate to the current route? Might be a good
|
|
@@ -605,27 +603,32 @@ update config appMsg model =
|
|
|
605
603
|
, actionData = newActionData
|
|
606
604
|
}
|
|
607
605
|
|
|
608
|
-
( userModel,
|
|
606
|
+
( userModel, userEffect ) =
|
|
609
607
|
-- TODO if urlWithoutRedirectResolution is different from the url with redirect resolution, then
|
|
610
608
|
-- instead of calling update, call pushUrl (I think?)
|
|
611
609
|
-- TODO include user Cmd
|
|
612
|
-
|
|
613
|
-
(
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
610
|
+
if stayingOnSamePath then
|
|
611
|
+
( previousPageData.userModel, NoEffect )
|
|
612
|
+
|
|
613
|
+
else
|
|
614
|
+
config.update model.pageFormState
|
|
615
|
+
(model.inFlightFetchers |> toFetcherState)
|
|
616
|
+
(model.transition |> Maybe.map Tuple.second)
|
|
617
|
+
newSharedData
|
|
618
|
+
newPageData
|
|
619
|
+
model.key
|
|
620
|
+
(config.onPageChange
|
|
621
|
+
{ protocol = model.url.protocol
|
|
622
|
+
, host = model.url.host
|
|
623
|
+
, port_ = model.url.port_
|
|
624
|
+
, path = urlPathToPath urlWithoutRedirectResolution
|
|
625
|
+
, query = urlWithoutRedirectResolution.query
|
|
626
|
+
, fragment = urlWithoutRedirectResolution.fragment
|
|
627
|
+
, metadata = config.urlToRoute urlWithoutRedirectResolution
|
|
628
|
+
}
|
|
629
|
+
)
|
|
630
|
+
previousPageData.userModel
|
|
631
|
+
|> Tuple.mapSecond UserCmd
|
|
629
632
|
|
|
630
633
|
updatedModel : Model userModel pageData actionData sharedData
|
|
631
634
|
updatedModel =
|
|
@@ -656,10 +659,13 @@ update config appMsg model =
|
|
|
656
659
|
, currentPath = newUrl.path
|
|
657
660
|
}
|
|
658
661
|
, if not stayingOnSamePath && scrollToTopWhenDone then
|
|
659
|
-
|
|
662
|
+
Batch
|
|
663
|
+
[ ScrollToTop
|
|
664
|
+
, userEffect
|
|
665
|
+
]
|
|
660
666
|
|
|
661
667
|
else
|
|
662
|
-
|
|
668
|
+
userEffect
|
|
663
669
|
)
|
|
664
670
|
|> (case maybeUserMsg of
|
|
665
671
|
Just userMsg ->
|
|
@@ -1392,7 +1398,8 @@ loadDataAndUpdateUrl ( newPageData, newSharedData, newActionData ) maybeUserMsg
|
|
|
1392
1398
|
, actionData = newActionData
|
|
1393
1399
|
}
|
|
1394
1400
|
|
|
1395
|
-
|
|
1401
|
+
-- TODO use userEffect here?
|
|
1402
|
+
( userModel, userEffect ) =
|
|
1396
1403
|
-- TODO if urlWithoutRedirectResolution is different from the url with redirect resolution, then
|
|
1397
1404
|
-- instead of calling update, call pushUrl (I think?)
|
|
1398
1405
|
-- TODO include user Cmd
|
|
@@ -65,7 +65,7 @@ type alias ProgramConfig userMsg userModel route pageData actionData sharedData
|
|
|
65
65
|
-> pageData
|
|
66
66
|
-> Maybe actionData
|
|
67
67
|
->
|
|
68
|
-
{ view : userModel -> { title : String, body : Html (Pages.Msg.Msg userMsg) }
|
|
68
|
+
{ view : userModel -> { title : String, body : List (Html (Pages.Msg.Msg userMsg)) }
|
|
69
69
|
, head : List Head.Tag
|
|
70
70
|
}
|
|
71
71
|
, handleRoute : route -> DataSource (Maybe NotFoundReason)
|
|
@@ -88,7 +88,10 @@ type alias ProgramConfig userMsg userModel route pageData actionData sharedData
|
|
|
88
88
|
}
|
|
89
89
|
-> userMsg
|
|
90
90
|
, apiRoutes :
|
|
91
|
-
(
|
|
91
|
+
(Maybe { indent : Int, newLines : Bool }
|
|
92
|
+
-> Html Never
|
|
93
|
+
-> String
|
|
94
|
+
)
|
|
92
95
|
-> List (ApiRoute.ApiRoute ApiRoute.Response)
|
|
93
96
|
, pathPatterns : List RoutePattern
|
|
94
97
|
, basePath : List String
|
|
@@ -98,7 +101,7 @@ type alias ProgramConfig userMsg userModel route pageData actionData sharedData
|
|
|
98
101
|
, encodeResponse : ResponseSketch pageData actionData sharedData -> Bytes.Encode.Encoder
|
|
99
102
|
, encodeAction : actionData -> Bytes.Encode.Encoder
|
|
100
103
|
, decodeResponse : Bytes.Decode.Decoder (ResponseSketch pageData actionData sharedData)
|
|
101
|
-
, globalHeadTags : Maybe ((Html Never -> String) -> DataSource (List Head.Tag))
|
|
104
|
+
, globalHeadTags : Maybe ((Maybe { indent : Int, newLines : Bool } -> Html Never -> String) -> DataSource (List Head.Tag))
|
|
102
105
|
, cmdToEffect : Cmd userMsg -> effect
|
|
103
106
|
, perform :
|
|
104
107
|
{ fetchRouteData :
|
package/src/Server/Session.elm
CHANGED
|
@@ -1,8 +1,109 @@
|
|
|
1
|
-
module Server.Session exposing
|
|
1
|
+
module Server.Session exposing
|
|
2
|
+
( withSession
|
|
3
|
+
, NotLoadedReason(..)
|
|
4
|
+
, Session, empty, get, insert, remove, update, withFlash
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
{-| You can manage server state with HTTP cookies using this Server.Session API. Server-rendered pages define a `Server.Request.Parser`
|
|
8
|
+
to choose which requests to respond to and how to extract structured data from the incoming request.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Using Sessions in a Request.Parser
|
|
12
|
+
|
|
13
|
+
Using these functions, you can store and read session data in cookies to maintain state between requests.
|
|
14
|
+
For example, TODO:
|
|
15
|
+
|
|
16
|
+
action : RouteParams -> Request.Parser (DataSource (Response ActionData ErrorPage))
|
|
17
|
+
action routeParams =
|
|
18
|
+
MySession.withSession
|
|
19
|
+
(Request.formDataWithServerValidation (form |> Form.initCombinedServer identity))
|
|
20
|
+
(\nameResultData session ->
|
|
21
|
+
nameResultData
|
|
22
|
+
|> DataSource.map
|
|
23
|
+
(\nameResult ->
|
|
24
|
+
case nameResult of
|
|
25
|
+
Err errors ->
|
|
26
|
+
( session
|
|
27
|
+
|> Result.withDefault Nothing
|
|
28
|
+
|> Maybe.withDefault Session.empty
|
|
29
|
+
, Response.render
|
|
30
|
+
{ errors = errors
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
Ok ( _, name ) ->
|
|
35
|
+
( session
|
|
36
|
+
|> Result.withDefault Nothing
|
|
37
|
+
|> Maybe.withDefault Session.empty
|
|
38
|
+
|> Session.insert "name" name
|
|
39
|
+
|> Session.withFlash "message" ("Welcome " ++ name ++ "!")
|
|
40
|
+
, Route.redirectTo Route.Greet
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
The elm-pages framework will manage signing these cookies using the `secrets : DataSource (List String)` you pass in.
|
|
46
|
+
That means that the values you set in your session will be directly visible to anyone who has access to the cookie
|
|
47
|
+
(so don't directly store sensitive data in your session). Since the session cookie is signed using the secret you provide,
|
|
48
|
+
the cookie will be invalidated if it is tampered with because it won't match when elm-pages verifies that it has been
|
|
49
|
+
signed with your secrets. Of course you need to provide secure secrets and treat your secrets with care.
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
### Rotating Secrets
|
|
53
|
+
|
|
54
|
+
The first String in `secrets : DataSource (List String)` will be used to sign sessions, while the remaining String's will
|
|
55
|
+
still be used to attempt to "unsign" the cookies. So if you have a single secret:
|
|
56
|
+
|
|
57
|
+
Session.withSession
|
|
58
|
+
{ name = "mysession"
|
|
59
|
+
, secrets =
|
|
60
|
+
DataSource.map List.singleton
|
|
61
|
+
(Env.expect "SESSION_SECRET2022-09-01")
|
|
62
|
+
, options = cookieOptions
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
Then you add a second secret
|
|
66
|
+
|
|
67
|
+
Session.withSession
|
|
68
|
+
{ name = "mysession"
|
|
69
|
+
, secrets =
|
|
70
|
+
DataSource.map2
|
|
71
|
+
(\newSecret oldSecret -> [ newSecret, oldSecret ])
|
|
72
|
+
(Env.expect "SESSION_SECRET2022-12-01")
|
|
73
|
+
(Env.expect "SESSION_SECRET2022-09-01")
|
|
74
|
+
, options = cookieOptions
|
|
75
|
+
}
|
|
2
76
|
|
|
3
|
-
|
|
77
|
+
The new secret (`2022-12-01`) will be used to sign all requests. This API always re-signs using the newest secret in the list
|
|
78
|
+
whenever a new request comes in (even if the Session key-value pairs are unchanged), so these cookies get "refreshed" with the latest
|
|
79
|
+
signing secret when a new request comes in.
|
|
4
80
|
|
|
5
|
-
|
|
81
|
+
However, incoming requests with a cookie signed using the old secret (`2022-09-01`) will still successfully be unsigned
|
|
82
|
+
because they are still in the rotation (and then subsequently "refreshed" and signed using the new secret).
|
|
83
|
+
|
|
84
|
+
This allows you to rotate your session secrets (for security purposes). When a secret goes out of the rotation,
|
|
85
|
+
it will invalidate all cookies signed with that. For example, if we remove our old secret from the rotation:
|
|
86
|
+
|
|
87
|
+
Session.withSession
|
|
88
|
+
{ name = "mysession"
|
|
89
|
+
, secrets =
|
|
90
|
+
DataSource.map List.singleton
|
|
91
|
+
(Env.expect "SESSION_SECRET2022-12-01")
|
|
92
|
+
, options = cookieOptions
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
And then a user makes a request but had a session signed with our old secret (`2022-09-01`), the session will be invalid
|
|
96
|
+
(so `withSession` would parse the session for that request as `Nothing`). It's standard for cookies to have an expiration date,
|
|
97
|
+
so there's nothing wrong with an old session expiring (and the browser will eventually delete old cookies), just be aware of that when rotating secrets.
|
|
98
|
+
|
|
99
|
+
@docs withSession
|
|
100
|
+
|
|
101
|
+
@docs NotLoadedReason
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
## Creating and Updating Sessions
|
|
105
|
+
|
|
106
|
+
@docs Session, empty, get, insert, remove, update, withFlash
|
|
6
107
|
|
|
7
108
|
-}
|
|
8
109
|
|
|
@@ -10,10 +111,9 @@ import DataSource exposing (DataSource)
|
|
|
10
111
|
import DataSource.Http
|
|
11
112
|
import DataSource.Internal.Request
|
|
12
113
|
import Dict exposing (Dict)
|
|
13
|
-
import Dict.Extra
|
|
14
114
|
import Json.Decode
|
|
15
115
|
import Json.Encode
|
|
16
|
-
import Server.Request
|
|
116
|
+
import Server.Request
|
|
17
117
|
import Server.Response exposing (Response)
|
|
18
118
|
import Server.SetCookie as SetCookie
|
|
19
119
|
|
|
@@ -23,11 +123,6 @@ type Session
|
|
|
23
123
|
= Session (Dict String Value)
|
|
24
124
|
|
|
25
125
|
|
|
26
|
-
{-| -}
|
|
27
|
-
type alias Decoder decoded =
|
|
28
|
-
Json.Decode.Decoder decoded
|
|
29
|
-
|
|
30
|
-
|
|
31
126
|
{-| -}
|
|
32
127
|
type Value
|
|
33
128
|
= Persistent String
|
|
@@ -113,20 +208,13 @@ empty =
|
|
|
113
208
|
|
|
114
209
|
{-| -}
|
|
115
210
|
type NotLoadedReason
|
|
116
|
-
=
|
|
117
|
-
|
|
|
211
|
+
= NoSessionCookie
|
|
212
|
+
| InvalidSessionCookie
|
|
118
213
|
|
|
119
214
|
|
|
120
215
|
{-| -}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
constructor
|
|
124
|
-
|> Json.Decode.succeed
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
{-| -}
|
|
128
|
-
setValues : Session -> Json.Encode.Value
|
|
129
|
-
setValues (Session session) =
|
|
216
|
+
encodeNonExpiringPairs : Session -> Json.Encode.Value
|
|
217
|
+
encodeNonExpiringPairs (Session session) =
|
|
130
218
|
session
|
|
131
219
|
|> Dict.toList
|
|
132
220
|
|> List.filterMap
|
|
@@ -151,71 +239,56 @@ flashPrefix =
|
|
|
151
239
|
"__flash__"
|
|
152
240
|
|
|
153
241
|
|
|
154
|
-
{-| -}
|
|
155
|
-
clearFlashCookies : Dict String String -> Dict String String
|
|
156
|
-
clearFlashCookies dict =
|
|
157
|
-
Dict.Extra.removeWhen
|
|
158
|
-
(\key _ ->
|
|
159
|
-
key |> String.startsWith flashPrefix
|
|
160
|
-
)
|
|
161
|
-
dict
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
{-| -}
|
|
165
|
-
expectSession :
|
|
166
|
-
{ name : String
|
|
167
|
-
, secrets : DataSource (List String)
|
|
168
|
-
, sameSite : String
|
|
169
|
-
}
|
|
170
|
-
-> Parser request
|
|
171
|
-
-> (request -> Result () Session -> DataSource ( Session, Response data errorPage ))
|
|
172
|
-
-> Parser (DataSource (Response data errorPage))
|
|
173
|
-
expectSession config userRequest toRequest =
|
|
174
|
-
Request.map2
|
|
175
|
-
(\sessionCookie userRequestData ->
|
|
176
|
-
sessionCookie
|
|
177
|
-
|> decryptCookie config
|
|
178
|
-
|> DataSource.andThen
|
|
179
|
-
(encodeSessionUpdate config toRequest userRequestData)
|
|
180
|
-
)
|
|
181
|
-
(Request.expectCookie config.name)
|
|
182
|
-
userRequest
|
|
183
|
-
|
|
184
|
-
|
|
185
242
|
{-| -}
|
|
186
243
|
withSession :
|
|
187
244
|
{ name : String
|
|
188
245
|
, secrets : DataSource (List String)
|
|
189
|
-
,
|
|
246
|
+
, options : SetCookie.Options
|
|
190
247
|
}
|
|
191
|
-
->
|
|
192
|
-
->
|
|
193
|
-
-> Parser (DataSource (Response data errorPage))
|
|
194
|
-
withSession config userRequest
|
|
195
|
-
Request.map2
|
|
248
|
+
-> (request -> Result NotLoadedReason Session -> DataSource ( Session, Response data errorPage ))
|
|
249
|
+
-> Server.Request.Parser request
|
|
250
|
+
-> Server.Request.Parser (DataSource (Response data errorPage))
|
|
251
|
+
withSession config toRequest userRequest =
|
|
252
|
+
Server.Request.map2
|
|
196
253
|
(\maybeSessionCookie userRequestData ->
|
|
197
254
|
let
|
|
198
|
-
|
|
199
|
-
|
|
255
|
+
unsigned : DataSource (Result NotLoadedReason Session)
|
|
256
|
+
unsigned =
|
|
200
257
|
case maybeSessionCookie of
|
|
201
258
|
Just sessionCookie ->
|
|
202
259
|
sessionCookie
|
|
203
|
-
|>
|
|
204
|
-
|> DataSource.map
|
|
260
|
+
|> unsignCookie config
|
|
261
|
+
|> DataSource.map
|
|
262
|
+
(\unsignResult ->
|
|
263
|
+
case unsignResult of
|
|
264
|
+
Ok decoded ->
|
|
265
|
+
Ok decoded
|
|
266
|
+
|
|
267
|
+
Err () ->
|
|
268
|
+
Err InvalidSessionCookie
|
|
269
|
+
)
|
|
205
270
|
|
|
206
271
|
Nothing ->
|
|
207
|
-
|
|
272
|
+
Err NoSessionCookie
|
|
208
273
|
|> DataSource.succeed
|
|
209
274
|
in
|
|
210
|
-
|
|
275
|
+
unsigned
|
|
211
276
|
|> DataSource.andThen
|
|
212
277
|
(encodeSessionUpdate config toRequest userRequestData)
|
|
213
278
|
)
|
|
214
|
-
(Request.cookie config.name)
|
|
279
|
+
(Server.Request.cookie config.name)
|
|
215
280
|
userRequest
|
|
216
281
|
|
|
217
282
|
|
|
218
|
-
encodeSessionUpdate :
|
|
283
|
+
encodeSessionUpdate :
|
|
284
|
+
{ name : String
|
|
285
|
+
, secrets : DataSource (List String)
|
|
286
|
+
, options : SetCookie.Options
|
|
287
|
+
}
|
|
288
|
+
-> (c -> d -> DataSource ( Session, Response data errorPage ))
|
|
289
|
+
-> c
|
|
290
|
+
-> d
|
|
291
|
+
-> DataSource (Response data errorPage)
|
|
219
292
|
encodeSessionUpdate config toRequest userRequestData sessionResult =
|
|
220
293
|
sessionResult
|
|
221
294
|
|> toRequest userRequestData
|
|
@@ -225,25 +298,18 @@ encodeSessionUpdate config toRequest userRequestData sessionResult =
|
|
|
225
298
|
(\encoded ->
|
|
226
299
|
response
|
|
227
300
|
|> Server.Response.withSetCookieHeader
|
|
228
|
-
(SetCookie.setCookie config.name encoded
|
|
229
|
-
|> SetCookie.httpOnly
|
|
230
|
-
|> SetCookie.withPath "/"
|
|
231
|
-
-- TODO set expiration time
|
|
232
|
-
-- TODO do I need to encrypt the session expiration as part of it
|
|
233
|
-
-- TODO should I update the expiration time every time?
|
|
234
|
-
--|> SetCookie.withExpiration (Time.millisToPosix 100000000000)
|
|
235
|
-
)
|
|
301
|
+
(SetCookie.setCookie config.name encoded config.options)
|
|
236
302
|
)
|
|
237
|
-
(
|
|
238
|
-
(
|
|
303
|
+
(sign config.secrets
|
|
304
|
+
(encodeNonExpiringPairs sessionUpdate)
|
|
239
305
|
)
|
|
240
306
|
)
|
|
241
307
|
|
|
242
308
|
|
|
243
|
-
|
|
244
|
-
|
|
309
|
+
unsignCookie : { a | secrets : DataSource (List String) } -> String -> DataSource (Result () Session)
|
|
310
|
+
unsignCookie config sessionCookie =
|
|
245
311
|
sessionCookie
|
|
246
|
-
|>
|
|
312
|
+
|> unsign config.secrets (Json.Decode.dict Json.Decode.string)
|
|
247
313
|
|> DataSource.map
|
|
248
314
|
(Result.map
|
|
249
315
|
(\dict ->
|
|
@@ -265,8 +331,8 @@ decryptCookie config sessionCookie =
|
|
|
265
331
|
)
|
|
266
332
|
|
|
267
333
|
|
|
268
|
-
|
|
269
|
-
|
|
334
|
+
sign : DataSource (List String) -> Json.Encode.Value -> DataSource String
|
|
335
|
+
sign getSecrets input =
|
|
270
336
|
getSecrets
|
|
271
337
|
|> DataSource.andThen
|
|
272
338
|
(\secrets ->
|
|
@@ -293,8 +359,8 @@ encrypt getSecrets input =
|
|
|
293
359
|
)
|
|
294
360
|
|
|
295
361
|
|
|
296
|
-
|
|
297
|
-
|
|
362
|
+
unsign : DataSource (List String) -> Json.Decode.Decoder a -> String -> DataSource (Result () a)
|
|
363
|
+
unsign getSecrets decoder input =
|
|
298
364
|
getSecrets
|
|
299
365
|
|> DataSource.andThen
|
|
300
366
|
(\secrets ->
|