aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/guff/api/methods.cr8
-rw-r--r--src/guff/api/test.cr63
-rw-r--r--src/guff/handler.cr22
-rw-r--r--src/guff/handlers.cr5
-rw-r--r--src/guff/handlers/api.cr11
-rw-r--r--src/guff/handlers/session.cr21
-rw-r--r--src/guff/handlers/test-auth.cr10
-rw-r--r--src/guff/migrations.cr18
-rw-r--r--src/guff/models.cr7
-rw-r--r--src/guff/models/session.cr93
10 files changed, 220 insertions, 38 deletions
diff --git a/src/guff/api/methods.cr b/src/guff/api/methods.cr
index 0247460..8df48d5 100644
--- a/src/guff/api/methods.cr
+++ b/src/guff/api/methods.cr
@@ -357,6 +357,14 @@ module Guff
"set_user": {
text: "Set mock user",
+
+ args: {
+ "user_id": {
+ text: "ID of user",
+ type: :int,
+ required: true,
+ },
+ },
},
}
}
diff --git a/src/guff/api/test.cr b/src/guff/api/test.cr
index 44b5e10..79d2d2e 100644
--- a/src/guff/api/test.cr
+++ b/src/guff/api/test.cr
@@ -17,41 +17,56 @@ module Guff
[{foo: "bar"}, {foo: "asdf"}].to_json
end
- MOCK_USERS = {
- "users": [{
- "id": "0",
- "name": "Guest",
- "active": true,
- }, {
- "id": "1",
- "name": "Admin",
- "active": false,
- }, {
- "id": "2",
- "name": "Test User 1",
- "active": false,
- }, {
- "id": "2",
- "name": "Test User 2",
- "active": false,
- }, {
- "id": "3",
- "name": "Test User<<<<>>>>>&&&&&&2",
- "active": false,
- }]
- }
+ MOCK_USERS = [{
+ "id": "0",
+ "name": "Guest",
+ "active": true,
+ }, {
+ "id": "1",
+ "name": "Admin",
+ "active": false,
+ }, {
+ "id": "2",
+ "name": "Test User 1",
+ "active": false,
+ }, {
+ "id": "3",
+ "name": "Test User 2",
+ "active": false,
+ }, {
+ "id": "4",
+ "name": "Test User<<<<>>>>>&&&&&&2",
+ "active": false,
+ }]
private def do_test_get_users(
context : HTTP::Server::Context,
args : Hash(String, String)
)
- MOCK_USERS
+ user_id = "0"
+ with_session(context) do |session|
+ user_id = session["user_id"]? || "0"
+ false
+ end
+
+ # build result
+ users = MOCK_USERS.map { |row|
+ row.merge({ "active": (row["id"] == user_id) })
+ }
+
+ # return result
+ { "users": users }
end
private def do_test_set_user(
context : HTTP::Server::Context,
args : Hash(String, String)
)
+ with_session(context) do |session|
+ session["user_id"] = args["user_id"]
+ true
+ end
+
{ ok: true }
end
diff --git a/src/guff/handler.cr b/src/guff/handler.cr
index 357ed1f..52269a3 100644
--- a/src/guff/handler.cr
+++ b/src/guff/handler.cr
@@ -12,6 +12,28 @@ module Guff
call_next(context)
end
+ def with_session(context, &block : Hash(String, String) -> Bool)
+ sid, data = nil, nil
+ if sid = context.request.headers["x-guff-session-id"]?
+ data = Hash(String, String).from_json(@models.session.get(sid) || "{}")
+ else
+ sid = @models.session.add
+ context.request.headers["x-guff-session-id"] = sid
+ data = {} of String => String
+ end
+
+ save = block.call(data)
+
+ # save data
+ @models.session.set(sid, data.to_json) if save
+ end
+
+ private def get_session(context) : Hash(String, String)
+ end
+
+ private def save_session(sid, data)
+ end
+
private def redirect(context, url : String)
context.response.status_code = 302
context.response.headers["location"] = url
diff --git a/src/guff/handlers.cr b/src/guff/handlers.cr
index e573673..1e0e6fc 100644
--- a/src/guff/handlers.cr
+++ b/src/guff/handlers.cr
@@ -26,6 +26,11 @@ module Guff
}, {
env: %w{development},
init: ->(models : Models) {
+ SessionHandler.new(models) as HTTP::Handler
+ },
+ }, {
+ env: %w{development},
+ init: ->(models : Models) {
TestAuthHandler.new(models) as HTTP::Handler
},
}, {
diff --git a/src/guff/handlers/api.cr b/src/guff/handlers/api.cr
index 4401f75..6f5c528 100644
--- a/src/guff/handlers/api.cr
+++ b/src/guff/handlers/api.cr
@@ -10,8 +10,17 @@ private macro define_method_calls(hash)
case method
{% for method in methods %}
when "{{ method }}"
+ params = case context.request.method
+ when "GET"
+ context.request.query_params
+ when "POST"
+ HTTP::Params.parse((context.request.body || "") as String)
+ else
+ raise "unsupported method"
+ end
+
do_{{ namespace.id }}_{{ method }}(context, get_method_args(
- context.request.query_params,
+ params,
"{{ namespace.id }}",
"{{ method.id }}"
)).to_json
diff --git a/src/guff/handlers/session.cr b/src/guff/handlers/session.cr
new file mode 100644
index 0000000..bc7a5bc
--- /dev/null
+++ b/src/guff/handlers/session.cr
@@ -0,0 +1,21 @@
+require "json"
+require "../handler"
+
+class Guff::Handlers::SessionHandler < Guff::Handler
+ def call(context : HTTP::Server::Context)
+ if cookie = context.request.cookies["guff_session"]?
+ # get session id
+ sid = cookie.value
+ puts "DEBUG: session_id = #{sid}"
+ context.request.headers["x-guff-session-id"] = sid
+ end
+
+ call_next(context)
+
+ if sid = context.request.headers["x-guff-session-id"]?
+ # FIXME: need way to delete cookie
+ cookie = HTTP::Cookie.new("guff_session", sid)
+ context.response.headers["set-cookie"] = cookie.to_set_cookie_header
+ end
+ end
+end
diff --git a/src/guff/handlers/test-auth.cr b/src/guff/handlers/test-auth.cr
index 5cf5769..a0238f2 100644
--- a/src/guff/handlers/test-auth.cr
+++ b/src/guff/handlers/test-auth.cr
@@ -8,8 +8,6 @@ class Guff::Handlers::TestAuthHandler < Guff::Handler
case context.request.method
when "GET", "HEAD"
draw_page(context)
- when "POST"
- set_auth(context)
else
raise "unknown method"
end
@@ -21,12 +19,4 @@ class Guff::Handlers::TestAuthHandler < Guff::Handler
private def draw_page(context)
TestAuthHTMLView.run(@models, context)
end
-
- private def set_auth(context)
- params = HTTP::Params.parse(context.request.body as String)
-
- # TODO: extract user id and set it in session
-
- redirect(context, "/test/blog")
- end
end
diff --git a/src/guff/migrations.cr b/src/guff/migrations.cr
index 0e50fdb..c72edde 100644
--- a/src/guff/migrations.cr
+++ b/src/guff/migrations.cr
@@ -126,5 +126,23 @@ module Guff
}, %{
CREATE INDEX in_post_tags_post_id ON post_tags(post_id)
}],
+ }, {
+ id: "5-sessions",
+
+ sql: [%{
+ CREATE TABLE sessions (
+ session_id TEXT PRIMARY KEY,
+
+ created_at TIMESTAMP WITH TIME ZONE
+ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+
+ updated_at TIMESTAMP WITH TIME ZONE
+ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+
+ data TEXT NOT NULL DEFAULT ''
+ )
+ }, %{
+ CREATE INDEX in_sessions_sid on sessions(session_id)
+ }],
}]
end
diff --git a/src/guff/models.cr b/src/guff/models.cr
index 52437d4..e9102b5 100644
--- a/src/guff/models.cr
+++ b/src/guff/models.cr
@@ -30,9 +30,10 @@ module Guff
end
define_model_getters({
- post: PostModel,
- tag: TagModel,
- site: SiteModel,
+ post: PostModel,
+ tag: TagModel,
+ site: SiteModel,
+ session: SessionModel,
})
end
end
diff --git a/src/guff/models/session.cr b/src/guff/models/session.cr
new file mode 100644
index 0000000..1b88002
--- /dev/null
+++ b/src/guff/models/session.cr
@@ -0,0 +1,93 @@
+require "secure_random"
+require "../model"
+
+class Guff::SessionModel < Guff::Model
+ SQL = TemplateCache.new({
+ add: "
+ INSERT INTO sessions(session_id) VALUES (:session_id)
+ ",
+
+ delete: "
+ DELETE FROM sessions WHERE session_id = :session_id
+ ",
+
+ get: "
+ SELECT data
+
+ FROM sessions
+
+ WHERE session_id = :session_id
+ AND strftime('%s', datetime(updated_at, :age)) >=
+ strftime('%s', 'now')
+
+ ",
+
+ set: "
+ UPDATE sessions
+
+ SET data = :data,
+ updated_at = CURRENT_TIMESTAMP
+
+ WHERE session_id = :session_id
+ AND strftime('%s', datetime(updated_at, :age)) >=
+ strftime('%s', 'now')
+
+ ",
+
+ scrub: "
+ DELETE FROM sessions
+ WHERE strftime('%s', datetime(updated_at, :age)) <
+ strftime('%s', 'now')
+ ",
+ })
+
+ # maximum session age
+ AGE = "24 hours"
+
+ def initialize(models : Models)
+ super(models, SQL)
+
+ # TODO: make this configurable
+ @age = AGE
+ end
+
+ def add : String
+ # create sid
+ r = SecureRandom.hex(16)
+
+ # add session
+ query(:add, {
+ "session_id": r
+ }, nil)
+
+ # return sid
+ r
+ end
+
+ def get(session_id : String) : String?
+ one(:get, {
+ "session_id": session_id,
+ "age": @age,
+ }, nil)
+ end
+
+ def set(session_id : String, data : String)
+ query(:set, {
+ "session_id": session_id,
+ "age": @age,
+ "data": data,
+ }, nil)
+ end
+
+ def delete(session_id : String)
+ query(:delete, {
+ "session_id": sesion_id,
+ }, nil)
+ end
+
+ private def scrub
+ query(:scrub, {
+ "age": @age,
+ }, nil)
+ end
+end