Start page flow code

db4
Slava Pestov 2008-04-26 05:49:41 -05:00
parent ca77a729d8
commit 42bc93f66e
17 changed files with 178 additions and 87 deletions

View File

@ -13,6 +13,7 @@ http.server.auth.providers
http.server.auth.providers.null http.server.auth.providers.null
http.server.actions http.server.actions
http.server.components http.server.components
http.server.flows
http.server.forms http.server.forms
http.server.sessions http.server.sessions
http.server.boilerplate http.server.boilerplate
@ -22,7 +23,6 @@ http.server.validators ;
IN: http.server.auth.login IN: http.server.auth.login
QUALIFIED: smtp QUALIFIED: smtp
SYMBOL: post-login-url
SYMBOL: login-failed? SYMBOL: login-failed?
TUPLE: login < dispatcher users ; TUPLE: login < dispatcher users ;
@ -60,8 +60,7 @@ M: user-saver dispose
: successful-login ( user -- response ) : successful-login ( user -- response )
logged-in-user sset logged-in-user sset
post-login-url sget "$login" or f <permanent-redirect> "$login" end-flow ;
f post-login-url sset ;
:: <login-action> ( -- action ) :: <login-action> ( -- action )
[let | form [ <login-form> ] | [let | form [ <login-form> ] |
@ -155,8 +154,6 @@ SYMBOL: user-exists?
"verify-password" <password> add-field "verify-password" <password> add-field
"email" <email> add-field ; "email" <email> add-field ;
SYMBOL: previous-page
:: <edit-profile-action> ( -- action ) :: <edit-profile-action> ( -- action )
[let | form [ <edit-profile-form> ] | [let | form [ <edit-profile-form> ] |
<action> <action>
@ -196,7 +193,7 @@ SYMBOL: previous-page
user-profile-changed? on user-profile-changed? on
previous-page sget f <permanent-redirect> "$login" end-flow
] >>submit ] >>submit
] ; ] ;
@ -342,14 +339,15 @@ TUPLE: protected responder ;
C: <protected> protected C: <protected> protected
M: protected init-session* responder>> init-session* ;
: show-login-page ( -- response ) : show-login-page ( -- response )
request get request-url post-login-url sset begin-flow
"$login/login" f <temporary-redirect> ; "$login/login" f <temporary-redirect> ;
M: protected call-responder ( path responder -- response ) M: protected call-responder ( path responder -- response )
logged-in-user sget dup [ logged-in-user sget dup [
save-user-after save-user-after
request get request-url previous-page sset
responder>> call-responder responder>> call-responder
] [ ] [
3drop 3drop

View File

@ -5,6 +5,7 @@ io io.streams.string arrays
html.elements html.elements
http http
http.server http.server
http.server.sessions
http.server.templating ; http.server.templating ;
IN: http.server.boilerplate IN: http.server.boilerplate
@ -12,6 +13,8 @@ TUPLE: boilerplate responder template ;
: <boilerplate> f boilerplate boa ; : <boilerplate> f boilerplate boa ;
M: boilerplate init-session* responder>> init-session* ;
SYMBOL: title SYMBOL: title
: set-title ( string -- ) : set-title ( string -- )

View File

@ -1,11 +1,13 @@
! Copyright (C) 2008 Slava Pestov. ! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: db http.server kernel accessors USING: db http.server http.server.sessions kernel accessors
continuations namespaces destructors ; continuations namespaces destructors ;
IN: http.server.db IN: http.server.db
TUPLE: db-persistence responder db params ; TUPLE: db-persistence responder db params ;
M: db-persistence init-session* responder>> init-session* ;
C: <db-persistence> db-persistence C: <db-persistence> db-persistence
: connect-db ( db-persistence -- ) : connect-db ( db-persistence -- )

View File

@ -0,0 +1,50 @@
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: accessors namespaces sequences arrays kernel
assocs assocs.lib hashtables math.parser
html.elements http http.server http.server.sessions ;
IN: http.server.flows
TUPLE: flows responder ;
C: <flows> flows
: begin-flow* ( -- id )
request get [ path>> ] [ query>> ] bi 2array
flows sget set-at-unique
session-changed ;
: end-flow* ( default id -- response )
flows sget at [ first2 ] [ f ] ?if <permanent-redirect> ;
SYMBOL: flow-id
: flow-id-key "factorflowid" ;
: begin-flow ( -- )
begin-flow* flow-id set ;
: end-flow ( default -- response )
flow-id get end-flow* ;
: add-flow-id ( query -- query' )
flow-id get [ flow-id-key associate assoc-union ] when* ;
: flow-form-field ( -- )
flow-id get [
<input
"hidden" =type
flow-id-key =name
=value
input/>
] when* ;
M: flows call-responder
[ add-flow-id ] add-link-hook
[ flow-form-field ] add-form-hook
flow-id-key request-params at flow-id set
responder>> call-responder ;
M: flows init-session*
H{ } clone flows sset
responder>> init-session* ;

View File

@ -69,8 +69,11 @@ SYMBOL: base-paths
SYMBOL: link-hook SYMBOL: link-hook
: add-link-hook ( quot -- )
link-hook [ compose ] change ; inline
: modify-query ( query -- query ) : modify-query ( query -- query )
link-hook get [ ] or call ; link-hook get call ;
: base-path ( string -- path ) : base-path ( string -- path )
dup base-paths get at dup base-paths get at
@ -93,8 +96,11 @@ SYMBOL: link-hook
SYMBOL: form-hook SYMBOL: form-hook
: add-form-hook ( quot -- )
form-hook [ compose ] change ;
: hidden-form-field ( -- ) : hidden-form-field ( -- )
form-hook get [ ] or call ; form-hook get call ;
: absolute-redirect ( to query -- url ) : absolute-redirect ( to query -- url )
#! Same host. #! Same host.
@ -226,6 +232,9 @@ SYMBOL: exit-continuation
: do-request ( request -- response ) : do-request ( request -- response )
[ [
H{ } clone base-paths set H{ } clone base-paths set
[ ] link-hook set
[ ] form-hook set
[ log-request ] [ log-request ]
[ request set ] [ request set ]
[ path>> split-path main-responder get call-responder ] tri [ path>> split-path main-responder get call-responder ] tri

View File

@ -3,7 +3,7 @@ USING: tools.test http http.server.sessions
http.server.sessions.storage http.server.sessions.storage.db http.server.sessions.storage http.server.sessions.storage.db
http.server.actions http.server math namespaces kernel accessors http.server.actions http.server math namespaces kernel accessors
prettyprint io.streams.string io.files splitting destructors prettyprint io.streams.string io.files splitting destructors
sequences db db.sqlite ; sequences db db.sqlite continuations ;
: with-session : with-session
[ [
@ -49,8 +49,12 @@ M: foo call-responder
"text/plain" <content> exit-with "text/plain" <content> exit-with
] >>display ; ] >>display ;
[ "auth-test.db" temp-file sqlite-db delete-file ] ignore-errors
"auth-test.db" temp-file sqlite-db [ "auth-test.db" temp-file sqlite-db [
init-sessions-table
[ [
empty-session empty-session
123 >>id session set 123 >>id session set

View File

@ -10,11 +10,7 @@ http.server.sessions.storage.null
html.elements ; html.elements ;
IN: http.server.sessions IN: http.server.sessions
! ! ! ! ! ! TUPLE: session id expiry namespace changed? ;
! WARNING: this session manager is vulnerable to XSRF attacks
! ! ! ! ! !
TUPLE: session id user-agent client-addr namespace ;
: <session> ( id -- session ) : <session> ( id -- session )
session new session new
@ -24,6 +20,8 @@ GENERIC: init-session* ( responder -- )
M: object init-session* drop ; M: object init-session* drop ;
M: dispatcher init-session* default>> init-session* ;
TUPLE: session-manager responder sessions ; TUPLE: session-manager responder sessions ;
: new-session-manager ( responder class -- responder' ) : new-session-manager ( responder class -- responder' )
@ -31,18 +29,23 @@ TUPLE: session-manager responder sessions ;
null-sessions >>sessions null-sessions >>sessions
swap >>responder ; inline swap >>responder ; inline
SYMBOL: session-changed? : (session-changed) ( session -- )
t >>changed? drop ;
: session-changed ( -- )
session get (session-changed) ;
: sget ( key -- value ) : sget ( key -- value )
session get namespace>> at ; session get namespace>> at ;
: sset ( value key -- ) : sset ( value key -- )
session get namespace>> set-at session get
session-changed? on ; [ namespace>> set-at ] [ (session-changed) ] bi ;
: schange ( key quot -- ) : schange ( key quot -- )
session get namespace>> swap change-at session get
session-changed? on ; inline [ namespace>> swap change-at ] keep
(session-changed) ; inline
: sessions session-manager get sessions>> ; : sessions session-manager get sessions>> ;
@ -51,11 +54,18 @@ SYMBOL: session-changed?
: init-session ( session managed -- ) : init-session ( session managed -- )
>r session r> '[ , init-session* ] with-variable ; >r session r> '[ , init-session* ] with-variable ;
: timeout 20 minutes ;
: cutoff-time ( -- time )
now timeout time+ timestamp>millis ;
: touch-session ( session -- )
cutoff-time >>expiry drop ;
: empty-session ( -- session ) : empty-session ( -- session )
f <session> f <session>
"" >>user-agent H{ } clone >>namespace
"" >>client-addr dup touch-session ;
H{ } clone >>namespace ;
: begin-session ( responder -- session ) : begin-session ( responder -- session )
>r empty-session r> >r empty-session r>
@ -70,8 +80,9 @@ TUPLE: session-saver session ;
C: <session-saver> session-saver C: <session-saver> session-saver
M: session-saver dispose M: session-saver dispose
session-changed? get session>> dup changed?>> [
[ session>> sessions update-session ] [ drop ] if ; [ touch-session ] [ sessions update-session ] bi
] [ drop ] if ;
: save-session-after ( session -- ) : save-session-after ( session -- )
<session-saver> add-always-destructor ; <session-saver> add-always-destructor ;
@ -80,14 +91,6 @@ M: session-saver dispose
[ save-session-after ] [ session set ] bi [ save-session-after ] [ session set ] bi
[ session-manager set ] [ responder>> call-responder ] bi ; [ session-manager set ] [ responder>> call-responder ] bi ;
TUPLE: null-sessions < session-manager ;
: <null-sessions> ( responder -- manager )
null-sessions new-session-manager ;
M: null-sessions call-responder ( path responder -- response )
<session> call-responder/session ;
TUPLE: url-sessions < session-manager ; TUPLE: url-sessions < session-manager ;
: <url-sessions> ( responder -- responder' ) : <url-sessions> ( responder -- responder' )
@ -105,9 +108,8 @@ TUPLE: url-sessions < session-manager ;
: session-form-field ( -- ) : session-form-field ( -- )
<input <input
"hidden" =type "hidden" =type
session-id-key =id
session-id-key =name session-id-key =name
session get id>> =value session get id>> number>string =value
input/> ; input/> ;
: new-url-session ( path responder -- response ) : new-url-session ( path responder -- response )
@ -115,8 +117,8 @@ TUPLE: url-sessions < session-manager ;
<temporary-redirect> ; <temporary-redirect> ;
M: url-sessions call-responder ( path responder -- response ) M: url-sessions call-responder ( path responder -- response )
[ add-session-id ] link-hook set [ add-session-id ] add-link-hook
[ session-form-field ] form-hook set [ session-form-field ] add-form-hook
dup current-url-session [ dup current-url-session [
call-responder/session call-responder/session
] [ ] [

View File

@ -1,8 +1,8 @@
! Copyright (C) 2008 Slava Pestov. ! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: assocs accessors kernel http.server.sessions.storage USING: assocs accessors kernel http.server.sessions.storage
http.server.sessions http.server db.tuples db.types math.parser http.server.sessions http.server db db.tuples db.types math.parser
classes.singleton random ; math.intervals fry random calendar sequences alarms ;
IN: http.server.sessions.storage.db IN: http.server.sessions.storage.db
SINGLETON: sessions-in-db SINGLETON: sessions-in-db
@ -11,8 +11,7 @@ session "SESSIONS"
{ {
! { "id" "ID" +random-id+ system-random-generator } ! { "id" "ID" +random-id+ system-random-generator }
{ "id" "ID" INTEGER +native-id+ } { "id" "ID" INTEGER +native-id+ }
{ "user-agent" "USERAGENT" { VARCHAR 256 } +not-null+ } { "expiry" "EXPIRY" BIG-INTEGER +not-null+ }
{ "client-addr" "CLIENTADDR" { VARCHAR 256 } +not-null+ }
{ "namespace" "NAMESPACE" FACTOR-BLOB } { "namespace" "NAMESPACE" FACTOR-BLOB }
} define-persistent } define-persistent
@ -29,3 +28,13 @@ M: sessions-in-db delete-session ( id storage -- )
M: sessions-in-db new-session ( session storage -- ) M: sessions-in-db new-session ( session storage -- )
drop insert-tuple ; drop insert-tuple ;
: expired-sessions ( -- session )
f <session>
USE: math now timestamp>millis [ 60 60 * 1000 * - ] keep [a,b] >>expiry
select-tuples ;
: start-expiring-sessions ( db seq -- )
'[
, , [ expired-sessions [ delete-tuple ] each ] with-db
] 5 minutes every drop ;

View File

@ -3,8 +3,6 @@
USING: calendar ; USING: calendar ;
IN: http.server.sessions.storage IN: http.server.sessions.storage
: timeout 20 minutes ;
GENERIC: get-session ( id storage -- session ) GENERIC: get-session ( id storage -- session )
GENERIC: update-session ( session storage -- ) GENERIC: update-session ( session storage -- )

View File

@ -4,6 +4,7 @@ io io.files io.encodings.utf8 html.elements unicode.case
tuple-syntax xml xml.data xml.writer xml.utilities tuple-syntax xml xml.data xml.writer xml.utilities
http.server http.server
http.server.auth http.server.auth
http.server.flows
http.server.components http.server.components
http.server.sessions http.server.sessions
http.server.templating http.server.templating
@ -83,14 +84,33 @@ SYMBOL: tags
dup empty? dup empty?
[ drop f ] [ "," split [ dup value ] H{ } map>assoc ] if ; [ drop f ] [ "," split [ dup value ] H{ } map>assoc ] if ;
: a-flow-attr ( tag -- )
"flow" optional-attr {
{ "none" [ flow-id off ] }
{ "begin" [ begin-flow ] }
{ "current" [ ] }
{ f [ ] }
} case ;
: a-session-attr ( tag -- )
"session" optional-attr {
{ "none" [ session off flow-id off ] }
{ "current" [ ] }
{ f [ ] }
} case ;
: a-start-tag ( tag -- ) : a-start-tag ( tag -- )
[
<a <a
dup a-flow-attr
dup a-session-attr
dup "value" optional-attr [ value f ] [ dup "value" optional-attr [ value f ] [
[ "href" required-attr ] [ "href" required-attr ]
[ "query" optional-attr parse-query-attr ] [ "query" optional-attr parse-query-attr ]
bi bi
] ?if link>string =href ] ?if link>string =href
a> ; a>
] with-scope ;
: process-tag-children ( tag -- ) : process-tag-children ( tag -- )
[ process-template ] each ; [ process-template ] each ;

View File

@ -4,6 +4,7 @@ USING: accessors kernel sequences assocs io.files io.sockets
namespaces db db.sqlite smtp namespaces db db.sqlite smtp
http.server http.server
http.server.db http.server.db
http.server.flows
http.server.sessions http.server.sessions
http.server.auth.login http.server.auth.login
http.server.auth.providers.db http.server.auth.providers.db
@ -20,27 +21,6 @@ IN: webapps.factor-website
: factor-template ( path -- template ) : factor-template ( path -- template )
"resource:extra/webapps/factor-website/" swap ".xml" 3append <chloe> ; "resource:extra/webapps/factor-website/" swap ".xml" 3append <chloe> ;
: <factor-boilerplate> ( responder -- responder' )
<login>
users-in-db >>users
allow-registration
allow-password-recovery
allow-edit-profile
<boilerplate>
"page" factor-template >>template
<url-sessions>
sessions-in-db >>sessions
test-db <db-persistence> ;
: <pastebin-app> ( -- responder )
<pastebin> <factor-boilerplate> ;
: <planet-app> ( -- responder )
<planet-factor> <factor-boilerplate> ;
: <todo-app> ( -- responder )
<todo-list> <protected> <factor-boilerplate> ;
: init-factor-db ( -- ) : init-factor-db ( -- )
test-db [ test-db [
init-users-table init-users-table
@ -56,9 +36,20 @@ IN: webapps.factor-website
: <factor-website> ( -- responder ) : <factor-website> ( -- responder )
<dispatcher> <dispatcher>
<todo-app> "todo" add-responder <todo-list> "todo" add-responder
<pastebin-app> "pastebin" add-responder <pastebin> "pastebin" add-responder
<planet-app> "planet" add-responder ; <planet-factor> "planet" add-responder
<login>
users-in-db >>users
allow-registration
allow-password-recovery
allow-edit-profile
<boilerplate>
"page" factor-template >>template
<flows>
<url-sessions>
sessions-in-db >>sessions
test-db <db-persistence> ;
: init-factor-website ( -- ) : init-factor-website ( -- )
"factorcode.org" 25 <inet> smtp-server set-global "factorcode.org" 25 <inet> smtp-server set-global
@ -66,6 +57,9 @@ IN: webapps.factor-website
init-factor-db init-factor-db
<factor-website> main-responder set-global <factor-website> main-responder set-global ;
"planet" main-responder get responders>> at start-update-task ; : start-factor-website
test-db start-expiring-sessions
"planet" main-responder get responders>> at test-db start-update-task
8812 httpd ;

View File

@ -13,7 +13,7 @@
<t:comment> <t:comment>
<t:if code="http.server.auth.login:allow-edit-profile?"> <t:if code="http.server.auth.login:allow-edit-profile?">
| <t:a href="$login/edit-profile">Edit Profile</t:a> | <t:a href="$login/edit-profile" flow="begin">Edit Profile</t:a>
</t:if> </t:if>
<t:form action="$login/logout" class="inline"> <t:form action="$login/logout" class="inline">

View File

@ -4,7 +4,7 @@
<p class="news"> <p class="news">
<strong><t:view component="title" /></strong> <br/> <strong><t:view component="title" /></strong> <br/>
<t:a value="link" class="more">Read More...</t:a> <t:a value="link" session="none" class="more">Read More...</t:a>
</p> </p>
</t:chloe> </t:chloe>

View File

@ -3,7 +3,7 @@
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0"> <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<h2 class="posting-title"> <h2 class="posting-title">
<t:a value="link"><t:view component="title" /></t:a> <t:a value="link" session="none"><t:view component="title" /></t:a>
</h2> </h2>
<p class="posting-body"> <p class="posting-body">
@ -11,7 +11,7 @@
</p> </p>
<p class="posting-date"> <p class="posting-date">
<t:a value="link"><t:view component="pub-date" /></t:a> <t:a value="link" session="none"><t:view component="pub-date" /></t:a>
</p> </p>
</t:chloe> </t:chloe>

View File

@ -2,7 +2,7 @@
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: kernel accessors sequences sorting locals math USING: kernel accessors sequences sorting locals math
calendar alarms logging concurrency.combinators namespaces calendar alarms logging concurrency.combinators namespaces
sequences.lib db.types db.tuples db sequences.lib db.types db.tuples db fry
rss xml.writer rss xml.writer
http.server http.server
http.server.crud http.server.crud
@ -167,5 +167,7 @@ blog "BLOGS"
<boilerplate> <boilerplate>
"planet" planet-template >>template ; "planet" planet-template >>template ;
: start-update-task ( planet -- ) : start-update-task ( planet db seq -- )
[ update-cached-postings ] curry 10 minutes every drop ; '[
, , , [ update-cached-postings ] with-db
] 10 minutes every drop ;

View File

@ -14,7 +14,7 @@
<t:comment> <t:comment>
<t:if code="http.server.auth.login:allow-edit-profile?"> <t:if code="http.server.auth.login:allow-edit-profile?">
| <t:a href="$login/edit-profile">Edit Profile</t:a> | <t:a href="$login/edit-profile" flow="begin">Edit Profile</t:a>
</t:if> </t:if>
<t:form action="$login/logout" class="inline"> <t:form action="$login/logout" class="inline">

View File

@ -9,7 +9,7 @@
| <t:a href="$todo-list/edit">Add Item</t:a> | <t:a href="$todo-list/edit">Add Item</t:a>
<t:if code="http.server.auth.login:allow-edit-profile?"> <t:if code="http.server.auth.login:allow-edit-profile?">
| <t:a href="$login/edit-profile">Edit Profile</t:a> | <t:a href="$login/edit-profile" flow="begin">Edit Profile</t:a>
</t:if> </t:if>
<t:form action="$login/logout" class="inline"> <t:form action="$login/logout" class="inline">