Upsert user record from Tailscale identity headers
Sequel/SQLite wired up with auto-migrating DB connection. Tests use in-memory DB.
Assisted-by: Claude Opus 4.6 via pi
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2e274d9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+db/*.db
diff --git a/db/migrate/001_create_users.rb b/db/migrate/001_create_users.rb
new file mode 100644
index 0000000..6bfa8b0
--- /dev/null
+++ b/db/migrate/001_create_users.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+Sequel.migration do
+ change do
+ create_table(:users) do
+ primary_key :id
+ String :login, null: false, unique: true
+ String :name
+ DateTime :created_at, null: false
+ DateTime :updated_at, null: false
+ end
+ end
+end
diff --git a/lib/db.rb b/lib/db.rb
new file mode 100644
index 0000000..df86307
--- /dev/null
+++ b/lib/db.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require "sequel"
+
+DB = Sequel.sqlite(ENV.fetch("DATABASE_URL") { "db/ketchup.db" })
+Sequel.extension :migration
+Sequel::Migrator.run(DB, File.expand_path("../db/migrate", __dir__))
diff --git a/lib/web.rb b/lib/web.rb
index 7f5aca2..b0f85f2 100644
--- a/lib/web.rb
+++ b/lib/web.rb
@@ -2,6 +2,7 @@
require "roda"
+require_relative "db"
require_relative "views/series/new"
class Web < Roda
@@ -12,10 +13,14 @@ class Web < Roda
login = env["HTTP_TAILSCALE_USER_LOGIN"]
return unless login
- {
- login: login,
- name: env["HTTP_TAILSCALE_USER_NAME"],
- }
+ name = env["HTTP_TAILSCALE_USER_NAME"]
+ now = Time.now
+
+ DB[:users]
+ .insert_conflict(target: :login, update: { name: name, updated_at: now })
+ .insert(login: login, name: name, created_at: now, updated_at: now)
+
+ DB[:users].first(login: login)
end
route do |r|
diff --git a/test/test_web.rb b/test/test_web.rb
index a0eb8cd..0fade8b 100644
--- a/test/test_web.rb
+++ b/test/test_web.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+ENV["DATABASE_URL"] = ":memory:"
+
require "minitest/autorun"
require "rack/test"
@@ -22,6 +24,19 @@ class TestWeb < Minitest::Test
assert_includes last_response.body, "Alice"
end
+ def test_root_creates_user_record
+ get "/", {}, tailscale_headers(login: "bob@example.com", name: "Bob")
+ user = DB[:users].first(login: "bob@example.com")
+ assert_equal "Bob", user[:name]
+ end
+
+ def test_root_updates_user_name
+ get "/", {}, tailscale_headers(login: "carol@example.com", name: "Carol")
+ get "/", {}, tailscale_headers(login: "carol@example.com", name: "Carol C.")
+ user = DB[:users].first(login: "carol@example.com")
+ assert_equal "Carol C.", user[:name]
+ end
+
def test_root_requires_tailscale_user
get "/"
assert_equal 403, last_response.status