summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2025-04-03 23:45:23 -0400
committerOwen Jacobson <owen@grimoire.ca>2025-04-03 23:45:23 -0400
commit9f7f82dbd9adee8ae18ae7ff2600b3e1dc8fadbc (patch)
treed973d00486ffab3445e3ca454e93a941ed8fe6e2
parent24755a89a97a4d1cb10ebbcf41e200861f3bedf3 (diff)
parent45eea07a56022f647b3a273798a5255cda73f13d (diff)
Merge branch 'prop/rename-login-to-user'
-rw-r--r--.sqlx/query-0be9484c8d277b08a925b7776b5d0e847cf52c68f24f5ea878b897944e70254c.json (renamed from .sqlx/query-8b474c8ed7859f745888644db639b7a4a21210ebf0b7fba97cd016ff6ab4d769.json)4
-rw-r--r--.sqlx/query-17f6f507d9343a734e1098f6ce56d372edeb35f92769a0181a71d68a68780649.json (renamed from .sqlx/query-4623f989492e9eae6788ee9472f1138395b50cdfc9a28e9791e5484890f0201b.json)6
-rw-r--r--.sqlx/query-18aada5bab0b6c438b6b97ab85129c951fb199dadb66f5c980ec30405b74a277.json (renamed from .sqlx/query-1946af14f5d3da9af51fc0e3d4f25cff1556aec7083bc484172c58cbd655a316.json)6
-rw-r--r--.sqlx/query-1a6654e50f9cbfe09a0a75b9c3126c22b432df8756ab007191f60702de270878.json (renamed from .sqlx/query-3c7cca4f823bd5e4cb0562af0c246e75ce95f6eb10f705c16b22345d31aefc61.json)4
-rw-r--r--.sqlx/query-27c3ffb5c284cf2d1c7cee713f6354d6150088a052d1f261853f327a83e8dd75.json20
-rw-r--r--.sqlx/query-3d0354407ac4dcd7b012328b9086c4f582bd2b3dc99a6a8e5f4853e0d4c43c50.json (renamed from .sqlx/query-2e0aa3126267465ee1ae01e6856eff74a544f0a1c3692766e48a3182df5ada98.json)6
-rw-r--r--.sqlx/query-3ff8a089ca1c57111e8c0e8d6d9da73e88a6cab35ae511674d25aa78bac9bc04.json (renamed from .sqlx/query-53b1f14d450a99f486bfd79075e71bd7e30dc93d32e1f273c878f18f2984860f.json)6
-rw-r--r--.sqlx/query-53193435a6eeb72266d0e526a3c6adfcfa20b687ac886efe6d41d5abfd7bc183.json (renamed from .sqlx/query-584aea21a5ceb0ce6e48bc224bfc431f2e517755983f3510565e18ecb0e6e637.json)6
-rw-r--r--.sqlx/query-579bf0557d3e141dfd411c25c2ae66d6abb70f7bd2413cfbe23b71d1ce6090cd.json (renamed from .sqlx/query-72441293731853e9f0cc1141e4322f8026f9e2515b6bacaed81f6248c52a198a.json)6
-rw-r--r--.sqlx/query-613199c25e16a52d85761e213750fe202abfa2af5347010dee59d3b8075eb19e.json (renamed from .sqlx/query-0b1543ec93e02c48c5cbaafd391b5812dc2d1d4a52ea3072b5dd52d71637b33d.json)4
-rw-r--r--.sqlx/query-684b4bef3e553b8ccfa20d8de3272e44b1c15ed75977ce24f25fd62042fb20fb.json (renamed from .sqlx/query-6d34d8232e7247155c697f7ef7a26f6b14e1d30c3fb44ece8fb149c92317fa91.json)6
-rw-r--r--.sqlx/query-713ccbb31289eebd93840a7cf99b01395f30788ab60232f239ce85125e425b79.json (renamed from .sqlx/query-d49d4ab5f1bf4c78fa619680b04a506cd63a85741923f841b7c36c46b70a538f.json)6
-rw-r--r--.sqlx/query-79495da0101ad12e7e983666b2ca84cbe31458ff73a94648c30b62f3130a5a8b.json (renamed from .sqlx/query-903e7ec4fafd5ce124a0e40717bf42e5d43b041acb198710e75417ac40991ec6.json)6
-rw-r--r--.sqlx/query-9cf211b1f37708fc8f19b3af911f7488a708d5a50920bb042c0229c314ee3281.json (renamed from .sqlx/query-c31c02aa8c4e615c463835d188879a394d36e66f90edb27d2665f081ff95087f.json)6
-rw-r--r--.sqlx/query-9e4e8544c86e36901b4543d84b6ac46f63d2804ef3528d833e6d17bec8864454.json (renamed from .sqlx/query-2c20c29d9adfed6201a6a69da95bc8271dfc8c6ec8ebf174ba8a57111b322291.json)4
-rw-r--r--.sqlx/query-9eb1e7d793fe6a992dc937b041e5b5206628c3f0cd5230f6671bf8d8946d01f2.json (renamed from .sqlx/query-cbf29fae3725bbb3d9e94d932ace995f53efd6c7800a7a1b42daec41d081b3d2.json)4
-rw-r--r--.sqlx/query-afc6db503a3c49c18a9cb07cf0360789d46d94d9cd98887a3d9660d9b753d416.json (renamed from .sqlx/query-0f3bfb1ad8fad5213f733b32d8eb2d9c2bb4de2fbbf0b2280973966ef02f72b1.json)6
-rw-r--r--.sqlx/query-b18432e78891ffca0d7f3fcd1c543db4a3f02c211462704f7810fdbed924ac30.json (renamed from .sqlx/query-ff61ff22108f1e98bbfc9a84a27bdaefca99706a0c73c17f19cc537f3f669882.json)6
-rw-r--r--.sqlx/query-c2b0ff7e2f27b6970a16fbc233ed32638e853e3b8b8f8de26b53f90c98b6ce11.json20
-rw-r--r--.sqlx/query-e01508e57cc9cecc83640a5518d6364c8dbfdb45f205fecf0734fe272be493b0.json12
-rw-r--r--.sqlx/query-f9abb172f96bff3a5fb4ad29f3a52be8d3134fc68e37c6074c69970604ae3844.json12
-rw-r--r--.sqlx/query-fb7114754c6dc8ffe623ae0f3f61ec2e20795692db7019d962378c740ae69599.json (renamed from .sqlx/query-78d24fa907f3dcc0c129880e83b4ef41bad03b57937a27f98aebbef5268ef5f5.json)6
-rw-r--r--docs/api/authentication.md97
-rw-r--r--docs/api/boot.md108
-rw-r--r--docs/api/channels-messages.md52
-rw-r--r--docs/api/events.md145
-rw-r--r--docs/api/initial-setup.md38
-rw-r--r--docs/api/invitations.md102
-rw-r--r--migrations/20250323190045_rename_login_to_user.sql151
-rw-r--r--src/app.rs6
-rw-r--r--src/boot/app.rs16
-rw-r--r--src/boot/mod.rs4
-rw-r--r--src/boot/routes/get.rs6
-rw-r--r--src/boot/routes/test.rs12
-rw-r--r--src/channel/routes/channel/post.rs2
-rw-r--r--src/channel/routes/channel/test/delete.rs2
-rw-r--r--src/channel/routes/channel/test/post.rs2
-rw-r--r--src/cli.rs6
-rw-r--r--src/event/app.rs16
-rw-r--r--src/event/mod.rs12
-rw-r--r--src/event/routes/test/invite.rs20
-rw-r--r--src/event/routes/test/message.rs18
-rw-r--r--src/event/routes/test/resume.rs4
-rw-r--r--src/event/routes/test/setup.rs8
-rw-r--r--src/event/routes/test/token.rs14
-rw-r--r--src/invite/app.rs16
-rw-r--r--src/invite/mod.rs4
-rw-r--r--src/invite/repo.rs12
-rw-r--r--src/invite/routes/invite/post.rs4
-rw-r--r--src/invite/routes/invite/test/get.rs4
-rw-r--r--src/invite/routes/invite/test/post.rs32
-rw-r--r--src/invite/routes/post.rs2
-rw-r--r--src/invite/routes/test.rs2
-rw-r--r--src/lib.rs2
-rw-r--r--src/message/app.rs10
-rw-r--r--src/message/repo.rs16
-rw-r--r--src/message/routes/message/mod.rs2
-rw-r--r--src/message/routes/message/test.rs12
-rw-r--r--src/message/snapshot.rs4
-rw-r--r--src/setup/app.rs18
-rw-r--r--src/setup/repo.rs2
-rw-r--r--src/setup/routes/post.rs8
-rw-r--r--src/setup/routes/test.rs10
-rw-r--r--src/test/fixtures/cookie.rs2
-rw-r--r--src/test/fixtures/event.rs10
-rw-r--r--src/test/fixtures/identity.rs12
-rw-r--r--src/test/fixtures/invite.rs4
-rw-r--r--src/test/fixtures/message.rs4
-rw-r--r--src/test/fixtures/mod.rs2
-rw-r--r--src/test/fixtures/user.rs (renamed from src/test/fixtures/login.rs)18
-rw-r--r--src/token/app.rs36
-rw-r--r--src/token/extract/identity.rs6
-rw-r--r--src/token/repo/auth.rs22
-rw-r--r--src/token/repo/token.rs36
-rw-r--r--src/user/app.rs (renamed from src/login/app.rs)14
-rw-r--r--src/user/create.rs (renamed from src/login/create.rs)32
-rw-r--r--src/user/event.rs (renamed from src/login/event.rs)4
-rw-r--r--src/user/history.rs (renamed from src/login/history.rs)24
-rw-r--r--src/user/id.rs (renamed from src/login/id.rs)5
-rw-r--r--src/user/mod.rs (renamed from src/login/mod.rs)2
-rw-r--r--src/user/password.rs (renamed from src/login/password.rs)0
-rw-r--r--src/user/repo.rs (renamed from src/login/repo.rs)32
-rw-r--r--src/user/routes/login/mod.rs (renamed from src/login/routes/login/mod.rs)0
-rw-r--r--src/user/routes/login/post.rs (renamed from src/login/routes/login/post.rs)8
-rw-r--r--src/user/routes/login/test.rs (renamed from src/login/routes/login/test.rs)10
-rw-r--r--src/user/routes/logout/mod.rs (renamed from src/login/routes/logout/mod.rs)0
-rw-r--r--src/user/routes/logout/post.rs (renamed from src/login/routes/logout/post.rs)0
-rw-r--r--src/user/routes/logout/test.rs (renamed from src/login/routes/logout/test.rs)2
-rw-r--r--src/user/routes/mod.rs (renamed from src/login/routes/mod.rs)0
-rw-r--r--src/user/routes/password/mod.rs (renamed from src/login/routes/password/mod.rs)0
-rw-r--r--src/user/routes/password/post.rs (renamed from src/login/routes/password/post.rs)6
-rw-r--r--src/user/routes/password/test.rs (renamed from src/login/routes/password/test.rs)8
-rw-r--r--src/user/snapshot.rs (renamed from src/login/snapshot.rs)21
-rw-r--r--src/user/validate.rs (renamed from src/login/validate.rs)0
-rw-r--r--ui/lib/apiServer.js4
-rw-r--r--ui/lib/session.svelte.js10
-rw-r--r--ui/lib/state/remote/state.svelte.js24
-rw-r--r--ui/lib/state/remote/users.svelte.js (renamed from ui/lib/state/remote/logins.svelte.js)8
98 files changed, 851 insertions, 678 deletions
diff --git a/.sqlx/query-8b474c8ed7859f745888644db639b7a4a21210ebf0b7fba97cd016ff6ab4d769.json b/.sqlx/query-0be9484c8d277b08a925b7776b5d0e847cf52c68f24f5ea878b897944e70254c.json
index b433e4c..182f996 100644
--- a/.sqlx/query-8b474c8ed7859f745888644db639b7a4a21210ebf0b7fba97cd016ff6ab4d769.json
+++ b/.sqlx/query-0be9484c8d277b08a925b7776b5d0e847cf52c68f24f5ea878b897944e70254c.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n insert\n into token (id, secret, login, issued_at, last_used_at)\n values ($1, $2, $3, $4, $4)\n returning secret as \"secret!: Secret\"\n ",
+ "query": "\n insert\n into token (id, secret, user, issued_at, last_used_at)\n values ($1, $2, $3, $4, $4)\n returning secret as \"secret!: Secret\"\n ",
"describe": {
"columns": [
{
@@ -16,5 +16,5 @@
false
]
},
- "hash": "8b474c8ed7859f745888644db639b7a4a21210ebf0b7fba97cd016ff6ab4d769"
+ "hash": "0be9484c8d277b08a925b7776b5d0e847cf52c68f24f5ea878b897944e70254c"
}
diff --git a/.sqlx/query-4623f989492e9eae6788ee9472f1138395b50cdfc9a28e9791e5484890f0201b.json b/.sqlx/query-17f6f507d9343a734e1098f6ce56d372edeb35f92769a0181a71d68a68780649.json
index bfab6d4..0e67b03 100644
--- a/.sqlx/query-4623f989492e9eae6788ee9472f1138395b50cdfc9a28e9791e5484890f0201b.json
+++ b/.sqlx/query-17f6f507d9343a734e1098f6ce56d372edeb35f92769a0181a71d68a68780649.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n select\n message.channel as \"channel: channel::Id\",\n message.sender as \"sender: login::Id\",\n id as \"id: Id\",\n message.body as \"body: Body\",\n message.sent_at as \"sent_at: DateTime\",\n message.sent_sequence as \"sent_sequence: Sequence\",\n deleted.deleted_at as \"deleted_at?: DateTime\",\n deleted.deleted_sequence as \"deleted_sequence?: Sequence\"\n from message\n left join message_deleted as deleted\n using (id)\n where message.channel = $1\n and deleted.id is null\n ",
+ "query": "\n select\n message.channel as \"channel: channel::Id\",\n message.sender as \"sender: user::Id\",\n id as \"id: Id\",\n message.body as \"body: Body\",\n message.sent_at as \"sent_at: DateTime\",\n message.sent_sequence as \"sent_sequence: Sequence\",\n deleted.deleted_at as \"deleted_at?: DateTime\",\n deleted.deleted_sequence as \"deleted_sequence?: Sequence\"\n from message\n left join message_deleted as deleted\n using (id)\n where message.channel = $1\n and deleted.id is null\n ",
"describe": {
"columns": [
{
@@ -9,7 +9,7 @@
"type_info": "Text"
},
{
- "name": "sender: login::Id",
+ "name": "sender: user::Id",
"ordinal": 1,
"type_info": "Text"
},
@@ -58,5 +58,5 @@
false
]
},
- "hash": "4623f989492e9eae6788ee9472f1138395b50cdfc9a28e9791e5484890f0201b"
+ "hash": "17f6f507d9343a734e1098f6ce56d372edeb35f92769a0181a71d68a68780649"
}
diff --git a/.sqlx/query-1946af14f5d3da9af51fc0e3d4f25cff1556aec7083bc484172c58cbd655a316.json b/.sqlx/query-18aada5bab0b6c438b6b97ab85129c951fb199dadb66f5c980ec30405b74a277.json
index f765fda..040ada9 100644
--- a/.sqlx/query-1946af14f5d3da9af51fc0e3d4f25cff1556aec7083bc484172c58cbd655a316.json
+++ b/.sqlx/query-18aada5bab0b6c438b6b97ab85129c951fb199dadb66f5c980ec30405b74a277.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n insert into invite (id, issuer, issued_at)\n values ($1, $2, $3)\n returning\n id as \"id: Id\",\n issuer as \"issuer: login::Id\",\n issued_at as \"issued_at: DateTime\"\n ",
+ "query": "\n insert into invite (id, issuer, issued_at)\n values ($1, $2, $3)\n returning\n id as \"id: Id\",\n issuer as \"issuer: user::Id\",\n issued_at as \"issued_at: DateTime\"\n ",
"describe": {
"columns": [
{
@@ -9,7 +9,7 @@
"type_info": "Text"
},
{
- "name": "issuer: login::Id",
+ "name": "issuer: user::Id",
"ordinal": 1,
"type_info": "Text"
},
@@ -28,5 +28,5 @@
false
]
},
- "hash": "1946af14f5d3da9af51fc0e3d4f25cff1556aec7083bc484172c58cbd655a316"
+ "hash": "18aada5bab0b6c438b6b97ab85129c951fb199dadb66f5c980ec30405b74a277"
}
diff --git a/.sqlx/query-3c7cca4f823bd5e4cb0562af0c246e75ce95f6eb10f705c16b22345d31aefc61.json b/.sqlx/query-1a6654e50f9cbfe09a0a75b9c3126c22b432df8756ab007191f60702de270878.json
index 6aab5fc..2f114b8 100644
--- a/.sqlx/query-3c7cca4f823bd5e4cb0562af0c246e75ce95f6eb10f705c16b22345d31aefc61.json
+++ b/.sqlx/query-1a6654e50f9cbfe09a0a75b9c3126c22b432df8756ab007191f60702de270878.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n select count(*) > 0 as \"completed: bool\"\n from login\n ",
+ "query": "\n select count(*) > 0 as \"completed: bool\"\n from user\n ",
"describe": {
"columns": [
{
@@ -16,5 +16,5 @@
false
]
},
- "hash": "3c7cca4f823bd5e4cb0562af0c246e75ce95f6eb10f705c16b22345d31aefc61"
+ "hash": "1a6654e50f9cbfe09a0a75b9c3126c22b432df8756ab007191f60702de270878"
}
diff --git a/.sqlx/query-27c3ffb5c284cf2d1c7cee713f6354d6150088a052d1f261853f327a83e8dd75.json b/.sqlx/query-27c3ffb5c284cf2d1c7cee713f6354d6150088a052d1f261853f327a83e8dd75.json
new file mode 100644
index 0000000..c02667e
--- /dev/null
+++ b/.sqlx/query-27c3ffb5c284cf2d1c7cee713f6354d6150088a052d1f261853f327a83e8dd75.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "SQLite",
+ "query": "\n update user\n set password_hash = $1\n where id = $2\n returning id as \"id: Id\"\n ",
+ "describe": {
+ "columns": [
+ {
+ "name": "id: Id",
+ "ordinal": 0,
+ "type_info": "Text"
+ }
+ ],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "27c3ffb5c284cf2d1c7cee713f6354d6150088a052d1f261853f327a83e8dd75"
+}
diff --git a/.sqlx/query-2e0aa3126267465ee1ae01e6856eff74a544f0a1c3692766e48a3182df5ada98.json b/.sqlx/query-3d0354407ac4dcd7b012328b9086c4f582bd2b3dc99a6a8e5f4853e0d4c43c50.json
index 227d242..0481b7b 100644
--- a/.sqlx/query-2e0aa3126267465ee1ae01e6856eff74a544f0a1c3692766e48a3182df5ada98.json
+++ b/.sqlx/query-3d0354407ac4dcd7b012328b9086c4f582bd2b3dc99a6a8e5f4853e0d4c43c50.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n select\n id as \"id: Id\",\n message.channel as \"channel: channel::Id\",\n message.sender as \"sender: login::Id\",\n message.sent_at as \"sent_at: DateTime\",\n message.sent_sequence as \"sent_sequence: Sequence\",\n message.body as \"body: Body\",\n deleted.deleted_at as \"deleted_at?: DateTime\",\n deleted.deleted_sequence as \"deleted_sequence?: Sequence\"\n from message\n left join message_deleted as deleted\n using (id)\n where message.sent_at < $1\n and deleted.id is null\n ",
+ "query": "\n select\n id as \"id: Id\",\n message.channel as \"channel: channel::Id\",\n message.sender as \"sender: user::Id\",\n message.sent_at as \"sent_at: DateTime\",\n message.sent_sequence as \"sent_sequence: Sequence\",\n message.body as \"body: Body\",\n deleted.deleted_at as \"deleted_at?: DateTime\",\n deleted.deleted_sequence as \"deleted_sequence?: Sequence\"\n from message\n left join message_deleted as deleted\n using (id)\n where message.sent_at < $1\n and deleted.id is null\n ",
"describe": {
"columns": [
{
@@ -14,7 +14,7 @@
"type_info": "Text"
},
{
- "name": "sender: login::Id",
+ "name": "sender: user::Id",
"ordinal": 2,
"type_info": "Text"
},
@@ -58,5 +58,5 @@
false
]
},
- "hash": "2e0aa3126267465ee1ae01e6856eff74a544f0a1c3692766e48a3182df5ada98"
+ "hash": "3d0354407ac4dcd7b012328b9086c4f582bd2b3dc99a6a8e5f4853e0d4c43c50"
}
diff --git a/.sqlx/query-53b1f14d450a99f486bfd79075e71bd7e30dc93d32e1f273c878f18f2984860f.json b/.sqlx/query-3ff8a089ca1c57111e8c0e8d6d9da73e88a6cab35ae511674d25aa78bac9bc04.json
index 7ec6aac..ad364ea 100644
--- a/.sqlx/query-53b1f14d450a99f486bfd79075e71bd7e30dc93d32e1f273c878f18f2984860f.json
+++ b/.sqlx/query-3ff8a089ca1c57111e8c0e8d6d9da73e88a6cab35ae511674d25aa78bac9bc04.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n select\n id as \"id: Id\",\n message.channel as \"channel: channel::Id\",\n message.sender as \"sender: login::Id\",\n message.sent_at as \"sent_at: DateTime\",\n message.sent_sequence as \"sent_sequence: Sequence\",\n message.body as \"body: Body\",\n deleted.deleted_at as \"deleted_at?: DateTime\",\n deleted.deleted_sequence as \"deleted_sequence?: Sequence\"\n from message\n left join message_deleted as deleted\n using (id)\n where message.last_sequence > $1\n ",
+ "query": "\n select\n id as \"id: Id\",\n message.channel as \"channel: channel::Id\",\n message.sender as \"sender: user::Id\",\n message.sent_at as \"sent_at: DateTime\",\n message.sent_sequence as \"sent_sequence: Sequence\",\n message.body as \"body: Body\",\n deleted.deleted_at as \"deleted_at?: DateTime\",\n deleted.deleted_sequence as \"deleted_sequence?: Sequence\"\n from message\n left join message_deleted as deleted\n using (id)\n where message.last_sequence > $1\n ",
"describe": {
"columns": [
{
@@ -14,7 +14,7 @@
"type_info": "Text"
},
{
- "name": "sender: login::Id",
+ "name": "sender: user::Id",
"ordinal": 2,
"type_info": "Text"
},
@@ -58,5 +58,5 @@
false
]
},
- "hash": "53b1f14d450a99f486bfd79075e71bd7e30dc93d32e1f273c878f18f2984860f"
+ "hash": "3ff8a089ca1c57111e8c0e8d6d9da73e88a6cab35ae511674d25aa78bac9bc04"
}
diff --git a/.sqlx/query-584aea21a5ceb0ce6e48bc224bfc431f2e517755983f3510565e18ecb0e6e637.json b/.sqlx/query-53193435a6eeb72266d0e526a3c6adfcfa20b687ac886efe6d41d5abfd7bc183.json
index f443d9a..31c14de 100644
--- a/.sqlx/query-584aea21a5ceb0ce6e48bc224bfc431f2e517755983f3510565e18ecb0e6e637.json
+++ b/.sqlx/query-53193435a6eeb72266d0e526a3c6adfcfa20b687ac886efe6d41d5abfd7bc183.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n select\n invite.id as \"invite_id: Id\",\n issuer.id as \"issuer_id: login::Id\",\n issuer.display_name as \"issuer_name: nfc::String\",\n invite.issued_at as \"invite_issued_at: DateTime\"\n from invite\n join login as issuer on (invite.issuer = issuer.id)\n where invite.id = $1\n ",
+ "query": "\n select\n invite.id as \"invite_id: Id\",\n issuer.id as \"issuer_id: user::Id\",\n issuer.display_name as \"issuer_name: nfc::String\",\n invite.issued_at as \"invite_issued_at: DateTime\"\n from invite\n join user as issuer on (invite.issuer = issuer.id)\n where invite.id = $1\n ",
"describe": {
"columns": [
{
@@ -9,7 +9,7 @@
"type_info": "Text"
},
{
- "name": "issuer_id: login::Id",
+ "name": "issuer_id: user::Id",
"ordinal": 1,
"type_info": "Text"
},
@@ -34,5 +34,5 @@
false
]
},
- "hash": "584aea21a5ceb0ce6e48bc224bfc431f2e517755983f3510565e18ecb0e6e637"
+ "hash": "53193435a6eeb72266d0e526a3c6adfcfa20b687ac886efe6d41d5abfd7bc183"
}
diff --git a/.sqlx/query-72441293731853e9f0cc1141e4322f8026f9e2515b6bacaed81f6248c52a198a.json b/.sqlx/query-579bf0557d3e141dfd411c25c2ae66d6abb70f7bd2413cfbe23b71d1ce6090cd.json
index eb30352..27349d7 100644
--- a/.sqlx/query-72441293731853e9f0cc1141e4322f8026f9e2515b6bacaed81f6248c52a198a.json
+++ b/.sqlx/query-579bf0557d3e141dfd411c25c2ae66d6abb70f7bd2413cfbe23b71d1ce6090cd.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n insert into message\n (id, channel, sender, sent_at, sent_sequence, body, last_sequence)\n values ($1, $2, $3, $4, $5, $6, $7)\n returning\n id as \"id: Id\",\n channel as \"channel: channel::Id\",\n sender as \"sender: login::Id\",\n sent_at as \"sent_at: DateTime\",\n sent_sequence as \"sent_sequence: Sequence\",\n body as \"body: Body\"\n ",
+ "query": "\n insert into message\n (id, channel, sender, sent_at, sent_sequence, body, last_sequence)\n values ($1, $2, $3, $4, $5, $6, $7)\n returning\n id as \"id: Id\",\n channel as \"channel: channel::Id\",\n sender as \"sender: user::Id\",\n sent_at as \"sent_at: DateTime\",\n sent_sequence as \"sent_sequence: Sequence\",\n body as \"body: Body\"\n ",
"describe": {
"columns": [
{
@@ -14,7 +14,7 @@
"type_info": "Text"
},
{
- "name": "sender: login::Id",
+ "name": "sender: user::Id",
"ordinal": 2,
"type_info": "Text"
},
@@ -46,5 +46,5 @@
true
]
},
- "hash": "72441293731853e9f0cc1141e4322f8026f9e2515b6bacaed81f6248c52a198a"
+ "hash": "579bf0557d3e141dfd411c25c2ae66d6abb70f7bd2413cfbe23b71d1ce6090cd"
}
diff --git a/.sqlx/query-0b1543ec93e02c48c5cbaafd391b5812dc2d1d4a52ea3072b5dd52d71637b33d.json b/.sqlx/query-613199c25e16a52d85761e213750fe202abfa2af5347010dee59d3b8075eb19e.json
index 937b07e..1657efa 100644
--- a/.sqlx/query-0b1543ec93e02c48c5cbaafd391b5812dc2d1d4a52ea3072b5dd52d71637b33d.json
+++ b/.sqlx/query-613199c25e16a52d85761e213750fe202abfa2af5347010dee59d3b8075eb19e.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n delete\n from token\n where login = $1\n returning id as \"id: Id\"\n ",
+ "query": "\n delete\n from token\n where user = $1\n returning id as \"id: Id\"\n ",
"describe": {
"columns": [
{
@@ -16,5 +16,5 @@
false
]
},
- "hash": "0b1543ec93e02c48c5cbaafd391b5812dc2d1d4a52ea3072b5dd52d71637b33d"
+ "hash": "613199c25e16a52d85761e213750fe202abfa2af5347010dee59d3b8075eb19e"
}
diff --git a/.sqlx/query-6d34d8232e7247155c697f7ef7a26f6b14e1d30c3fb44ece8fb149c92317fa91.json b/.sqlx/query-684b4bef3e553b8ccfa20d8de3272e44b1c15ed75977ce24f25fd62042fb20fb.json
index 93a4093..d1c2732 100644
--- a/.sqlx/query-6d34d8232e7247155c697f7ef7a26f6b14e1d30c3fb44ece8fb149c92317fa91.json
+++ b/.sqlx/query-684b4bef3e553b8ccfa20d8de3272e44b1c15ed75977ce24f25fd62042fb20fb.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n update token\n set last_used_at = $1\n where secret = $2\n returning\n id as \"token: Id\",\n login as \"login: login::Id\"\n ",
+ "query": "\n update token\n set last_used_at = $1\n where secret = $2\n returning\n id as \"token: Id\",\n user as \"user: user::Id\"\n ",
"describe": {
"columns": [
{
@@ -9,7 +9,7 @@
"type_info": "Text"
},
{
- "name": "login: login::Id",
+ "name": "user: user::Id",
"ordinal": 1,
"type_info": "Text"
}
@@ -22,5 +22,5 @@
false
]
},
- "hash": "6d34d8232e7247155c697f7ef7a26f6b14e1d30c3fb44ece8fb149c92317fa91"
+ "hash": "684b4bef3e553b8ccfa20d8de3272e44b1c15ed75977ce24f25fd62042fb20fb"
}
diff --git a/.sqlx/query-d49d4ab5f1bf4c78fa619680b04a506cd63a85741923f841b7c36c46b70a538f.json b/.sqlx/query-713ccbb31289eebd93840a7cf99b01395f30788ab60232f239ce85125e425b79.json
index 3ec71e8..63ed2eb 100644
--- a/.sqlx/query-d49d4ab5f1bf4c78fa619680b04a506cd63a85741923f841b7c36c46b70a538f.json
+++ b/.sqlx/query-713ccbb31289eebd93840a7cf99b01395f30788ab60232f239ce85125e425b79.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n select\n id as \"id: Id\",\n issuer as \"issuer: login::Id\",\n issued_at as \"issued_at: DateTime\"\n from invite\n where id = $1\n ",
+ "query": "\n select\n id as \"id: Id\",\n issuer as \"issuer: user::Id\",\n issued_at as \"issued_at: DateTime\"\n from invite\n where id = $1\n ",
"describe": {
"columns": [
{
@@ -9,7 +9,7 @@
"type_info": "Text"
},
{
- "name": "issuer: login::Id",
+ "name": "issuer: user::Id",
"ordinal": 1,
"type_info": "Text"
},
@@ -28,5 +28,5 @@
false
]
},
- "hash": "d49d4ab5f1bf4c78fa619680b04a506cd63a85741923f841b7c36c46b70a538f"
+ "hash": "713ccbb31289eebd93840a7cf99b01395f30788ab60232f239ce85125e425b79"
}
diff --git a/.sqlx/query-903e7ec4fafd5ce124a0e40717bf42e5d43b041acb198710e75417ac40991ec6.json b/.sqlx/query-79495da0101ad12e7e983666b2ca84cbe31458ff73a94648c30b62f3130a5a8b.json
index cf1afec..8f9be21 100644
--- a/.sqlx/query-903e7ec4fafd5ce124a0e40717bf42e5d43b041acb198710e75417ac40991ec6.json
+++ b/.sqlx/query-79495da0101ad12e7e983666b2ca84cbe31458ff73a94648c30b62f3130a5a8b.json
@@ -1,10 +1,10 @@
{
"db_name": "SQLite",
- "query": "\n select\n id as \"id: login::Id\",\n display_name as \"display_name: String\",\n canonical_name as \"canonical_name: String\",\n created_sequence as \"created_sequence: Sequence\",\n created_at as \"created_at: DateTime\",\n password_hash as \"password_hash: StoredHash\"\n from login\n where canonical_name = $1\n ",
+ "query": "\n select\n id as \"id: user::Id\",\n display_name as \"display_name: String\",\n canonical_name as \"canonical_name: String\",\n created_sequence as \"created_sequence: Sequence\",\n created_at as \"created_at: DateTime\",\n password_hash as \"password_hash: StoredHash\"\n from user\n where canonical_name = $1\n ",
"describe": {
"columns": [
{
- "name": "id: login::Id",
+ "name": "id: user::Id",
"ordinal": 0,
"type_info": "Text"
},
@@ -46,5 +46,5 @@
false
]
},
- "hash": "903e7ec4fafd5ce124a0e40717bf42e5d43b041acb198710e75417ac40991ec6"
+ "hash": "79495da0101ad12e7e983666b2ca84cbe31458ff73a94648c30b62f3130a5a8b"
}
diff --git a/.sqlx/query-c31c02aa8c4e615c463835d188879a394d36e66f90edb27d2665f081ff95087f.json b/.sqlx/query-9cf211b1f37708fc8f19b3af911f7488a708d5a50920bb042c0229c314ee3281.json
index aa20875..0926b67 100644
--- a/.sqlx/query-c31c02aa8c4e615c463835d188879a394d36e66f90edb27d2665f081ff95087f.json
+++ b/.sqlx/query-9cf211b1f37708fc8f19b3af911f7488a708d5a50920bb042c0229c314ee3281.json
@@ -1,10 +1,10 @@
{
"db_name": "SQLite",
- "query": "\n select\n id as \"id: login::Id\",\n display_name as \"display_name: String\",\n canonical_name as \"canonical_name: String\",\n created_sequence as \"created_sequence: Sequence\",\n created_at as \"created_at: DateTime\"\n from login\n where id = $1\n ",
+ "query": "\n select\n id as \"id: user::Id\",\n display_name as \"display_name: String\",\n canonical_name as \"canonical_name: String\",\n created_sequence as \"created_sequence: Sequence\",\n created_at as \"created_at: DateTime\"\n from user\n where id = $1\n ",
"describe": {
"columns": [
{
- "name": "id: login::Id",
+ "name": "id: user::Id",
"ordinal": 0,
"type_info": "Text"
},
@@ -40,5 +40,5 @@
false
]
},
- "hash": "c31c02aa8c4e615c463835d188879a394d36e66f90edb27d2665f081ff95087f"
+ "hash": "9cf211b1f37708fc8f19b3af911f7488a708d5a50920bb042c0229c314ee3281"
}
diff --git a/.sqlx/query-2c20c29d9adfed6201a6a69da95bc8271dfc8c6ec8ebf174ba8a57111b322291.json b/.sqlx/query-9e4e8544c86e36901b4543d84b6ac46f63d2804ef3528d833e6d17bec8864454.json
index ae546ad..beacb24 100644
--- a/.sqlx/query-2c20c29d9adfed6201a6a69da95bc8271dfc8c6ec8ebf174ba8a57111b322291.json
+++ b/.sqlx/query-9e4e8544c86e36901b4543d84b6ac46f63d2804ef3528d833e6d17bec8864454.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n select\n id as \"id: Id\",\n display_name as \"display_name: String\",\n canonical_name as \"canonical_name: String\",\n created_sequence as \"created_sequence: Sequence\",\n created_at as \"created_at: DateTime\"\n from login\n where login.created_sequence > $1\n ",
+ "query": "\n select\n id as \"id: Id\",\n display_name as \"display_name: String\",\n canonical_name as \"canonical_name: String\",\n created_sequence as \"created_sequence: Sequence\",\n created_at as \"created_at: DateTime\"\n from user\n where created_sequence > $1\n ",
"describe": {
"columns": [
{
@@ -40,5 +40,5 @@
false
]
},
- "hash": "2c20c29d9adfed6201a6a69da95bc8271dfc8c6ec8ebf174ba8a57111b322291"
+ "hash": "9e4e8544c86e36901b4543d84b6ac46f63d2804ef3528d833e6d17bec8864454"
}
diff --git a/.sqlx/query-cbf29fae3725bbb3d9e94d932ace995f53efd6c7800a7a1b42daec41d081b3d2.json b/.sqlx/query-9eb1e7d793fe6a992dc937b041e5b5206628c3f0cd5230f6671bf8d8946d01f2.json
index 9c3c10e..45177f3 100644
--- a/.sqlx/query-cbf29fae3725bbb3d9e94d932ace995f53efd6c7800a7a1b42daec41d081b3d2.json
+++ b/.sqlx/query-9eb1e7d793fe6a992dc937b041e5b5206628c3f0cd5230f6671bf8d8946d01f2.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n select\n id as \"id: Id\",\n display_name as \"display_name: String\",\n canonical_name as \"canonical_name: String\",\n created_sequence as \"created_sequence: Sequence\",\n created_at as \"created_at: DateTime\"\n from login\n where created_sequence <= $1\n order by canonical_name\n ",
+ "query": "\n select\n id as \"id: Id\",\n display_name as \"display_name: String\",\n canonical_name as \"canonical_name: String\",\n created_sequence as \"created_sequence: Sequence\",\n created_at as \"created_at: DateTime\"\n from user\n where created_sequence <= $1\n order by canonical_name\n ",
"describe": {
"columns": [
{
@@ -40,5 +40,5 @@
false
]
},
- "hash": "cbf29fae3725bbb3d9e94d932ace995f53efd6c7800a7a1b42daec41d081b3d2"
+ "hash": "9eb1e7d793fe6a992dc937b041e5b5206628c3f0cd5230f6671bf8d8946d01f2"
}
diff --git a/.sqlx/query-0f3bfb1ad8fad5213f733b32d8eb2d9c2bb4de2fbbf0b2280973966ef02f72b1.json b/.sqlx/query-afc6db503a3c49c18a9cb07cf0360789d46d94d9cd98887a3d9660d9b753d416.json
index ffd81dc..b7e6c1b 100644
--- a/.sqlx/query-0f3bfb1ad8fad5213f733b32d8eb2d9c2bb4de2fbbf0b2280973966ef02f72b1.json
+++ b/.sqlx/query-afc6db503a3c49c18a9cb07cf0360789d46d94d9cd98887a3d9660d9b753d416.json
@@ -1,10 +1,10 @@
{
"db_name": "SQLite",
- "query": "\n select\n id as \"id: login::Id\",\n display_name as \"display_name: String\",\n canonical_name as \"canonical_name: String\",\n created_sequence as \"created_sequence: Sequence\",\n created_at as \"created_at: DateTime\",\n password_hash as \"password_hash: StoredHash\"\n from login\n where id = $1\n ",
+ "query": "\n select\n id as \"id: user::Id\",\n display_name as \"display_name: String\",\n canonical_name as \"canonical_name: String\",\n created_sequence as \"created_sequence: Sequence\",\n created_at as \"created_at: DateTime\",\n password_hash as \"password_hash: StoredHash\"\n from user\n where id = $1\n ",
"describe": {
"columns": [
{
- "name": "id: login::Id",
+ "name": "id: user::Id",
"ordinal": 0,
"type_info": "Text"
},
@@ -46,5 +46,5 @@
false
]
},
- "hash": "0f3bfb1ad8fad5213f733b32d8eb2d9c2bb4de2fbbf0b2280973966ef02f72b1"
+ "hash": "afc6db503a3c49c18a9cb07cf0360789d46d94d9cd98887a3d9660d9b753d416"
}
diff --git a/.sqlx/query-ff61ff22108f1e98bbfc9a84a27bdaefca99706a0c73c17f19cc537f3f669882.json b/.sqlx/query-b18432e78891ffca0d7f3fcd1c543db4a3f02c211462704f7810fdbed924ac30.json
index f38f49c..3ae7605 100644
--- a/.sqlx/query-ff61ff22108f1e98bbfc9a84a27bdaefca99706a0c73c17f19cc537f3f669882.json
+++ b/.sqlx/query-b18432e78891ffca0d7f3fcd1c543db4a3f02c211462704f7810fdbed924ac30.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n select\n message.channel as \"channel: channel::Id\",\n message.sender as \"sender: login::Id\",\n message.id as \"id: Id\",\n message.body as \"body: Body\",\n message.sent_at as \"sent_at: DateTime\",\n message.sent_sequence as \"sent_sequence: Sequence\",\n deleted.deleted_at as \"deleted_at?: DateTime\",\n deleted.deleted_sequence as \"deleted_sequence?: Sequence\"\n from message\n left join message_deleted as deleted\n using (id)\n where message.sent_sequence <= $1\n order by message.sent_sequence\n ",
+ "query": "\n select\n message.channel as \"channel: channel::Id\",\n message.sender as \"sender: user::Id\",\n message.id as \"id: Id\",\n message.body as \"body: Body\",\n message.sent_at as \"sent_at: DateTime\",\n message.sent_sequence as \"sent_sequence: Sequence\",\n deleted.deleted_at as \"deleted_at?: DateTime\",\n deleted.deleted_sequence as \"deleted_sequence?: Sequence\"\n from message\n left join message_deleted as deleted\n using (id)\n where message.sent_sequence <= $1\n order by message.sent_sequence\n ",
"describe": {
"columns": [
{
@@ -9,7 +9,7 @@
"type_info": "Text"
},
{
- "name": "sender: login::Id",
+ "name": "sender: user::Id",
"ordinal": 1,
"type_info": "Text"
},
@@ -58,5 +58,5 @@
false
]
},
- "hash": "ff61ff22108f1e98bbfc9a84a27bdaefca99706a0c73c17f19cc537f3f669882"
+ "hash": "b18432e78891ffca0d7f3fcd1c543db4a3f02c211462704f7810fdbed924ac30"
}
diff --git a/.sqlx/query-c2b0ff7e2f27b6970a16fbc233ed32638e853e3b8b8f8de26b53f90c98b6ce11.json b/.sqlx/query-c2b0ff7e2f27b6970a16fbc233ed32638e853e3b8b8f8de26b53f90c98b6ce11.json
deleted file mode 100644
index 4c99c42..0000000
--- a/.sqlx/query-c2b0ff7e2f27b6970a16fbc233ed32638e853e3b8b8f8de26b53f90c98b6ce11.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n update login\n set password_hash = $1\n where id = $2\n returning id as \"id: Id\"\n ",
- "describe": {
- "columns": [
- {
- "name": "id: Id",
- "ordinal": 0,
- "type_info": "Text"
- }
- ],
- "parameters": {
- "Right": 2
- },
- "nullable": [
- false
- ]
- },
- "hash": "c2b0ff7e2f27b6970a16fbc233ed32638e853e3b8b8f8de26b53f90c98b6ce11"
-}
diff --git a/.sqlx/query-e01508e57cc9cecc83640a5518d6364c8dbfdb45f205fecf0734fe272be493b0.json b/.sqlx/query-e01508e57cc9cecc83640a5518d6364c8dbfdb45f205fecf0734fe272be493b0.json
deleted file mode 100644
index 4efac0c..0000000
--- a/.sqlx/query-e01508e57cc9cecc83640a5518d6364c8dbfdb45f205fecf0734fe272be493b0.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n insert\n into login (id, display_name, canonical_name, password_hash, created_sequence, created_at)\n values ($1, $2, $3, $4, $5, $6)\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 6
- },
- "nullable": []
- },
- "hash": "e01508e57cc9cecc83640a5518d6364c8dbfdb45f205fecf0734fe272be493b0"
-}
diff --git a/.sqlx/query-f9abb172f96bff3a5fb4ad29f3a52be8d3134fc68e37c6074c69970604ae3844.json b/.sqlx/query-f9abb172f96bff3a5fb4ad29f3a52be8d3134fc68e37c6074c69970604ae3844.json
new file mode 100644
index 0000000..9d1bc77
--- /dev/null
+++ b/.sqlx/query-f9abb172f96bff3a5fb4ad29f3a52be8d3134fc68e37c6074c69970604ae3844.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n insert\n into user (id, display_name, canonical_name, password_hash, created_sequence, created_at)\n values ($1, $2, $3, $4, $5, $6)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 6
+ },
+ "nullable": []
+ },
+ "hash": "f9abb172f96bff3a5fb4ad29f3a52be8d3134fc68e37c6074c69970604ae3844"
+}
diff --git a/.sqlx/query-78d24fa907f3dcc0c129880e83b4ef41bad03b57937a27f98aebbef5268ef5f5.json b/.sqlx/query-fb7114754c6dc8ffe623ae0f3f61ec2e20795692db7019d962378c740ae69599.json
index 09440ca..7f1e1f3 100644
--- a/.sqlx/query-78d24fa907f3dcc0c129880e83b4ef41bad03b57937a27f98aebbef5268ef5f5.json
+++ b/.sqlx/query-fb7114754c6dc8ffe623ae0f3f61ec2e20795692db7019d962378c740ae69599.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n select\n message.channel as \"channel: channel::Id\",\n message.sender as \"sender: login::Id\",\n id as \"id: Id\",\n message.body as \"body: Body\",\n message.sent_at as \"sent_at: DateTime\",\n message.sent_sequence as \"sent_sequence: Sequence\",\n deleted.deleted_at as \"deleted_at?: DateTime\",\n deleted.deleted_sequence as \"deleted_sequence?: Sequence\"\n from message\n left join message_deleted as deleted\n using (id)\n where id = $1\n ",
+ "query": "\n select\n message.channel as \"channel: channel::Id\",\n message.sender as \"sender: user::Id\",\n id as \"id: Id\",\n message.body as \"body: Body\",\n message.sent_at as \"sent_at: DateTime\",\n message.sent_sequence as \"sent_sequence: Sequence\",\n deleted.deleted_at as \"deleted_at?: DateTime\",\n deleted.deleted_sequence as \"deleted_sequence?: Sequence\"\n from message\n left join message_deleted as deleted\n using (id)\n where id = $1\n ",
"describe": {
"columns": [
{
@@ -9,7 +9,7 @@
"type_info": "Text"
},
{
- "name": "sender: login::Id",
+ "name": "sender: user::Id",
"ordinal": 1,
"type_info": "Text"
},
@@ -58,5 +58,5 @@
false
]
},
- "hash": "78d24fa907f3dcc0c129880e83b4ef41bad03b57937a27f98aebbef5268ef5f5"
+ "hash": "fb7114754c6dc8ffe623ae0f3f61ec2e20795692db7019d962378c740ae69599"
}
diff --git a/docs/api/authentication.md b/docs/api/authentication.md
index 93a8e52..7b5ebd7 100644
--- a/docs/api/authentication.md
+++ b/docs/api/authentication.md
@@ -11,43 +11,45 @@ stateDiagram-v2
Authenticated --> Unauthenticated : Token expired
```
-Authentication associates each authenticated request with a login.
+Authentication associates each authenticated request with a user.
-To create logins, see [initial setup](./initial-setup.md) and [invitations](./invitations.md).
+To create users, see [initial setup](./initial-setup.md) and [invitations](./invitations.md).
## Names
<!-- This prose is duplicated in channels-messages.md. If you change it here, consider changing it there, too. -->
-The service handles login names using two separate forms.
+The service handles user names using two separate forms.
-The first form is as given in the request used to create the login. This form of the login name is used throughout the API, and the service will preserve the name as entered (other than applying normalization), so that users' preferences around capitalization and accent marks are preserved.
+The first form is as given in the request used to create the user. This form of the use name is used throughout the API, and the service will preserve the name as entered (other than applying normalization), so that users' preferences around capitalization and accent marks are preserved.
-The second form is a "canonical" form, used internally by the service to control uniqueness and match names to logins. The canonical form is both case-folded and normalized.
+The second form is a "canonical" form, used internally by the service to control uniqueness and match names to users. The canonical form is both case-folded and normalized.
The canonical form is not available to API clients, but its use has practical consequences:
-* Names that differ only by case or only by code point sequence are treated as the same name. If the name is in use, changing the capitalization or changing the sequence of combining marks will not allow the creation of a second "identical" login.
-* The login API accepts any name that canonicalizes to the form stored in the database, making login names effectively case-insensitive.
-
+* Names that differ only by case or only by code point sequence are treated as the same name. If the name is in use, changing the capitalization or changing the sequence of combining marks will not allow the creation of a second "identical" user.
+* The login API accepts any name that canonicalizes to the form stored in the database, making user names effectively case-insensitive and composition-insensitive.
## Identity tokens
-A login is primarily authenticated using its username and password. However, passwords are a sensitive, long-lived credential, and are also operationally expensive to verify; for routine access, requests are authenticated using an identity token, instead.
+A user is primarily authenticated using its username and password. However, passwords are a sensitive, long-lived credential, and are also operationally expensive to verify; for routine access, requests are authenticated using an identity token, instead.
-Tokens are issued by logging into the service, using the `/api/auth/login` endpoint. The `/api/auth/logout` endpoint immediately invalidates the token used to make a request to it. Tokens are also invalidated after seven days of inactivity.
+Tokens are issued by logging into the service, using the `/api/auth/login` endpoint. The
+`/api/auth/logout` endpoint immediately invalidates the token used to make a request to it. Tokens are also invalidated after seven days of inactivity.
-To authenticate a request, include `cookie: identity=TOKEN SECRET` header in the request. For browser-based clients, this may happen automatically.
+To authenticate a request, include
+`cookie: identity=TOKEN SECRET` header in the request. For browser-based clients, this may happen automatically.
## Authentication failures
-Unless the endpoint's documentation says otherwise, all endpoints require authentication. Making a request to any endpoint that requires authentication, either without a token, or with a token that is not valid or that has expired, causes the service to return a `401 Unauthorized` response, instead of the responses documented for the endpoint the request was intended for. The API will not take action on requests that fail authentication in this way.
+Unless the endpoint's documentation says otherwise, all endpoints require authentication. Making a request to any endpoint that requires authentication, either without a token, or with a token that is not valid or that has expired, causes the service to return a
+`401 Unauthorized` response, instead of the responses documented for the endpoint the request was intended for. The API will not take action on requests that fail authentication in this way.
## `POST /api/auth/login`
-Authenticates the user using their login name and password. The login must exist before calling this endpoint.
+Authenticates the user using their name and password. The user must exist before calling this endpoint.
**This endpoint does not require an `identity` cookie.**
@@ -55,50 +57,53 @@ Authenticates the user using their login name and password. The login must exist
```json
{
- "name": "example username",
- "password": "the plaintext password",
+ "name": "example username",
+ "password": "the plaintext password"
}
```
The request must have the following fields:
-| Field | Type | Description |
-|:-----------|:-------|:--|
-| `name` | string | The login's name. |
+| Field | Type | Description |
+|:-----------|:-------|:-------------------------------------|
+| `name` | string | The login's name. |
| `password` | string | The login's password, in plain text. |
### Success
<!-- This prose is duplicated by 03-initial-setup.md and in 04-invitations.md, with small changes for context. If you edit it here, edit it there, too. -->
-This endpoint will respond with a status of `200 Okay` when successful. The body of the response will be a JSON object describing the authenticated login:
+This endpoint will respond with a status of
+`200 Okay` when successful. The body of the response will be a JSON object describing the authenticated user:
```json
{
- "id": "Labcd1234",
- "name": "Andrea"
+ "id": "Uabcd1234",
+ "name": "Andrea"
}
```
The response will include the following fields:
-| Field | Type | Description |
-|:------------|:-------|:--|
-| `id` | string | The authenticated login's ID. |
-| `name` | string | The authenticated login's name. |
+| Field | Type | Description |
+|:-------|:-------|:-------------------------------|
+| `id` | string | The authenticated user's ID. |
+| `name` | string | The authenticated user's name. |
-The response will include a `Set-Cookie` header for the `identity` cookie, providing the client with a newly-minted identity token associated with the login identified in the request. This token's value must be kept confidential.
+The response will include a `Set-Cookie` header for the
+`identity` cookie, providing the client with a newly-minted identity token associated with the user identified in the request. This token's value must be kept confidential.
The cookie will expire if it is not used regularly.
### Authentication failure
-This endpoint will respond with a status of `401 Unauthorized` if the login name and password do not correspond to an existing login.
+This endpoint will respond with a status of
+`401 Unauthorized` if the login name and password do not correspond to an existing user.
## `POST /api/auth/logout`
-Invalidates the identity token used to make the request, logging the user out.
+Invalidates the identity token used to make the request, logging the caller out.
### Request
@@ -112,51 +117,55 @@ The request must be an empty JSON object.
This endpoint will respond with a status of `204 No Content` when successful.
-The response will include a `Set-Cookie` header that clears the `identity` cookie. Regardless of whether the client clears the cookie, the service also invalidates the token.
+The response will include a `Set-Cookie` header that clears the
+`identity` cookie. Regardless of whether the client clears the cookie, the service also invalidates the token.
## `POST /api/password`
-Changes the current login's password, and invalidate all outstanding identity tokens.
+Changes the current user's password, and invalidates all outstanding identity tokens.
### Request
```json
{
- "password": "my-old-password",
- "to": "my-new-password"
+ "password": "my-old-password",
+ "to": "my-new-password"
}
```
The request must have the following fields:
-| Field | Type | Description |
-|:-----------|:-------|:--|
+| Field | Type | Description |
+|:-----------|:-------|:-----------------------------------------------|
| `password` | string | The login's _current_ password, in plain text. |
-| `to` | string | The login's _new_ password, in plain text. |
+| `to` | string | The login's _new_ password, in plain text. |
### Success
-This endpoint will respond with a status of `200 Okay` when successful. The body of the response will be a JSON object describing the authenticated login:
+This endpoint will respond with a status of
+`200 Okay` when successful. The body of the response will be a JSON object describing the authenticated user:
```json
{
- "id": "Labcd1234",
- "name": "Andrea"
+ "id": "Uabcd1234",
+ "name": "Andrea"
}
```
The response will include the following fields:
-| Field | Type | Description |
-|:------------|:-------|:--|
-| `id` | string | The authenticated login's ID. |
-| `name` | string | The authenticated login's name. |
+| Field | Type | Description |
+|:-------|:-------|:-------------------------------|
+| `id` | string | The authenticated user's ID. |
+| `name` | string | The authenticated user's name. |
-The response will include a `Set-Cookie` header for the `identity` cookie, providing the client with a newly-minted identity token associated with the login identified in the request. This token's value must be kept confidential. All previously-created identity cookies will cease to be valid.
+The response will include a `Set-Cookie` header for the
+`identity` cookie, providing the client with a newly-minted identity token associated with the login identified in the request. This token's value must be kept confidential. All previously-created identity cookies will cease to be valid.
The cookie will expire if it is not used regularly.
### Authentication failure
-This endpoint will respond with a status of `400 Bad Request` if the `password` does not match the login's current password.
+This endpoint will respond with a status of `400 Bad Request` if the
+`password` does not match the login's current password.
diff --git a/docs/api/boot.md b/docs/api/boot.md
index 88f2d5b..0c2dc08 100644
--- a/docs/api/boot.md
+++ b/docs/api/boot.md
@@ -18,14 +18,12 @@ sequenceDiagram
API <<->>- Andrea: Disconnect
```
-
Client initialization serves three purposes:
-* It confirms that the client's [identity token](./authentication.md) is valid, and tells the client what login that token is associated with.
+* It confirms that the client's [identity token](./authentication.md) is valid, and tells the client what user that token is associated with.
* It provides an initial snapshot of the state of the service.
* It provides a resume point for the [event stream](./events.md), which allows clients to consume events starting from the moment the snapshot was created.
-
## `GET /api/boot`
Returns the information needed to initialize a client.
@@ -34,77 +32,77 @@ This method is also the recommended way to validate the client's identity token,
### Success
-This endpoint will respond with a status of `200 Okay` when successful. The body of the response will be a JSON object containing the initial state for the client:
+This endpoint will respond with a status of
+`200 Okay` when successful. The body of the response will be a JSON object containing the initial state for the client:
```json
{
- "login": {
- "name": "example username",
- "id": "L1234abcd",
- },
- "resume_point": 1312,
- "logins": [
- {
- "id": "L1234abcd",
- "name": "example username"
- }
- ],
- "channels": [
- {
- "name": "nonsense and such",
- "id": "C1234abcd",
- }
- ],
- "messages": [
- {
- "at": "2024-09-27T23:19:10.208147Z",
- "channel": "C1234abcd",
- "sender": "L1234abcd",
- "id": "M1312acab",
- "body": "beep"
- }
- ]
+ "user": {
+ "name": "example username",
+ "id": "U1234abcd"
+ },
+ "resume_point": 1312,
+ "users": [
+ {
+ "id": "U1234abcd",
+ "name": "example username"
+ }
+ ],
+ "channels": [
+ {
+ "name": "nonsense and such",
+ "id": "C1234abcd"
+ }
+ ],
+ "messages": [
+ {
+ "at": "2024-09-27T23:19:10.208147Z",
+ "channel": "C1234abcd",
+ "sender": "U1234abcd",
+ "id": "M1312acab",
+ "body": "beep"
+ }
+ ]
}
```
The response will include the following fields:
-| Field | Type | Description |
-|:---------------|:----------------|:--|
-| `login` | object | The details of the caller's identity. |
+| Field | Type | Description |
+|:---------------|:----------------|:-------------------------------------------------------------------------------------------------------------------------|
+| `user` | object | The details of the caller's identity. |
| `resume_point` | integer | A resume point for [events](./events.md), such that the event stream will begin immediately after the included snapshot. |
-| `logins` | array of object | A snapshot of the logins present in the service. |
-| `channels` | array of object | A snapshot of the channels present in the service. |
-| `messages` | array of object | A snapshot of the messages present in the service. |
+| `users` | array of object | A snapshot of the users present in the service. |
+| `channels` | array of object | A snapshot of the channels present in the service. |
+| `messages` | array of object | A snapshot of the messages present in the service. |
-The `login` object will include the following fields:
+The `user` object will include the following fields:
-| Field | Type | Description |
-|:-------|:-------|:--|
+| Field | Type | Description |
+|:-------|:-------|:-----------------------------------------|
| `name` | string | The name of the caller's login identity. |
-| `id` | string | The ID of the caller's login identity. |
-
+| `id` | string | The ID of the caller's login identity. |
-Each element of the `logins` array describes a distinct login, and will include the following fields:
+Each element of the `users` array describes a distinct user, and will include the following fields:
-| Field | Type | Description |
-|:-------|:-------|:--|
-| `name` | string | The name for the login. |
-| `id` | string | A unique identifier for the login. This can be used to associate the login with other events, or to make API calls targeting the login. |
+| Field | Type | Description |
+|:-------|:-------|:-------------------------------------------------------------------------------------------------------------------------------------|
+| `name` | string | The name for the user. |
+| `id` | string | A unique identifier for the user. This can be used to associate the user with other events, or to make API calls targeting the user. |
Each element of the `channels` array describes a distinct channel, and will include the following fields:
-| Field | Type | Description |
-|:-------|:-------|:--|
-| `name` | string | The name for the channel. |
+| Field | Type | Description |
+|:-------|:-------|:----------------------------------------------------------------------------------------------------------------------------------------------|
+| `name` | string | The name for the channel. |
| `id` | string | A unique identifier for the channel. This can be used to associate the channel with other events, or to make API calls targeting the channel. |
Each element of the `messages` array describes a distinct message, and will include the following fields:
-| Field | Type | Description |
-|:----------|:----------|:--|
-| `at` | timestamp | The moment the message was sent. |
-| `channel` | string | The ID of the channel the message was sent to. |
-| `sender` | string | The ID of the login that sent the message. |
+| Field | Type | Description |
+|:----------|:----------|:----------------------------------------------------------------------------------------------------------------------------------------------|
+| `at` | timestamp | The moment the message was sent. |
+| `channel` | string | The ID of the channel the message was sent to. |
+| `sender` | string | The ID of the user that sent the message. |
| `id` | string | A unique identifier for the message. This can be used to associate the message with other events, or to make API calls targeting the message. |
-| `body` | string | The text of the message. |
+| `body` | string | The text of the message. |
diff --git a/docs/api/channels-messages.md b/docs/api/channels-messages.md
index a3c90be..c5b90ab 100644
--- a/docs/api/channels-messages.md
+++ b/docs/api/channels-messages.md
@@ -31,25 +31,17 @@ Every channel has a unique name, chosen when the channel is created.
<!-- This prose is duplicated in authentication.md. If you change it here, consider changing it there, too. -->
The service handles channel names using two separate forms.
-The first form is as given in the request used to create the channel. This form of the channel name is used throughout
-the API, and the service will preserve the name as entered (other than applying normalization), so that users'
-preferences around capitalization and accent marks are preserved.
+The first form is as given in the request used to create the channel. This form of the channel name is used throughout the API, and the service will preserve the name as entered (other than applying normalization), so that users' preferences around capitalization and accent marks are preserved.
-The second form is a "canonical" form, used internally by the service to control uniqueness and match names to channels.
-The canonical form is both case-folded and normalized.
+The second form is a "canonical" form, used internally by the service to control uniqueness and match names to channels. The canonical form is both case-folded and normalized.
-The canonical form is not available to API clients, but its use has practical consequences. Names that differ only by
-case or only by code point sequence are treated as the same name. If the name is in use, changing the capitalization or
-changing the sequence of combining marks will not allow the creation of a second "identical" channel.
+The canonical form is not available to API clients, but its use has practical consequences. Names that differ only by case or only by code point sequence are treated as the same name. If the name is in use, changing the capitalization or changing the sequence of combining marks will not allow the creation of a second "identical" channel.
## Expiry and purging
-Both channels and messages expire after a time. Messages expire 90 days after being sent. Channels expire 90 days after
-the last message sent to them, or after creation if no messages are sent in that time.
+Both channels and messages expire after a time. Messages expire 90 days after being sent. Channels expire 90 days after the last message sent to them, or after creation if no messages are sent in that time.
-Deleted channels and messages, including those that have expired, are temporarily retained by the service, to allow
-clients that are not connected to receive the corresponding deletion [events](./events.md). To limit storage growth,
-deleted channels and messages are purged from the service seven days after they were deleted.
+Deleted channels and messages, including those that have expired, are temporarily retained by the service, to allow clients that are not connected to receive the corresponding deletion [events](./events.md). To limit storage growth, deleted channels and messages are purged from the service seven days after they were deleted.
## `POST /api/channels`
@@ -79,8 +71,8 @@ The proposed `name` must be valid. The precise definition of valid is still up i
### Success
-This endpoint will respond with a status of `202 Accepted` when successful. The body of the response will be a JSON
-object describing the new channel:
+This endpoint will respond with a status of
+`202 Accepted` when successful. The body of the response will be a JSON object describing the new channel:
```json
{
@@ -96,9 +88,7 @@ The response will have the following fields:
| `id` | string | A unique identifier for the channel. This can be used to associate the channel with events, or to make API calls targeting the channel. |
| `name` | string | The channel's name. |
-The returned name may not be identical to the name requested, as the name will be converted
-to [normalization form C](http://www.unicode.org/reports/tr15/) automatically. The returned name will include this
-normalization; the service will use the normalized name elsewhere, and does not store the originally requested name.
+The returned name may not be identical to the name requested, as the name will be converted to [normalization form C](http://www.unicode.org/reports/tr15/) automatically. The returned name will include this normalization; the service will use the normalized name elsewhere, and does not store the originally requested name.
When completed, the service will emit a [channel created](events.md#channel-created) event with the channel's ID.
@@ -136,14 +126,14 @@ The request must have the following fields:
### Success
-This endpoint will respond with a status of `202 Accepted` when successful. The body of the response will be a JSON
-object describing the newly-sent message:
+This endpoint will respond with a status of
+`202 Accepted` when successful. The body of the response will be a JSON object describing the newly-sent message:
```json
{
"at": "2024-10-19T04:37:09.467325Z",
"channel": "Cfqdn1234",
- "sender": "Labcd1234",
+ "sender": "Uabcd1234",
"id": "Mgh98yp75",
"body": "an elaborate example message"
}
@@ -155,13 +145,11 @@ The response will have the following fields:
|:----------|:----------|:----------------------------------------------------------------------------------------------------------------------------------------|
| `at` | timestamp | The moment the message was sent. |
| `channel` | string | The ID of the channel the message was sent to. |
-| `sender` | string | The ID of the login that sent the message. |
+| `sender` | string | The ID of the user that sent the message. |
| `id` | string | A unique identifier for the message. This can be used to associate the message with events, or to make API calls targeting the message. |
| `body` | string | The message's body. |
-The returned message body may not be identical to the body as sent, as the body will be converted
-to [normalization form C](http://www.unicode.org/reports/tr15/) automatically. The returned body will include this
-normalization; the service will use the normalized body elsewhere, and does not store the originally submitted body.
+The returned message body may not be identical to the body as sent, as the body will be converted to [normalization form C](http://www.unicode.org/reports/tr15/) automatically. The returned body will include this normalization; the service will use the normalized body elsewhere, and does not store the originally submitted body.
When completed, the service will emit a [message sent](events.md#message-sent) event with the message's ID.
@@ -173,8 +161,7 @@ This endpoint will respond with a status of `404 Not Found` if the channel ID is
Deletes a channel.
-Deleting a channel prevents it from receiving any further messages. The channel must be empty; to delete a channel with
-messages in it, delete the messages first (or wait for them to expire).
+Deleting a channel prevents it from receiving any further messages. The channel must be empty; to delete a channel with messages in it, delete the messages first (or wait for them to expire).
This endpoint requires the following path parameter:
@@ -184,8 +171,8 @@ This endpoint requires the following path parameter:
### Success
-This endpoint will respond with a status of `202 Accepted` when successful. The body of the response will be a JSON
-object describing the deleted channel:
+This endpoint will respond with a status of
+`202 Accepted` when successful. The body of the response will be a JSON object describing the deleted channel:
```json
{
@@ -199,8 +186,7 @@ The response will have the following fields:
|:------|:-------|:------------------|
| `id` | string | The channel's ID. |
-When completed, the service will emit a [message deleted](events.md#message-deleted) event for each message in the
-channel, followed by a [channel deleted](events.md#channel-deleted) event with the channel's ID.
+When completed, the service will emit a [message deleted](events.md#message-deleted) event for each message in the channel, followed by a [channel deleted](events.md#channel-deleted) event with the channel's ID.
### Channel not empty
@@ -222,8 +208,8 @@ This endpoint requires the following path parameter:
### Success
-This endpoint will respond with a status of `202 Accepted` when successful. The body of the response will be a JSON
-object describing the deleted message:
+This endpoint will respond with a status of
+`202 Accepted` when successful. The body of the response will be a JSON object describing the deleted message:
```json
{
diff --git a/docs/api/events.md b/docs/api/events.md
index b23469c..3347a26 100644
--- a/docs/api/events.md
+++ b/docs/api/events.md
@@ -28,12 +28,14 @@ sequenceDiagram
end
```
-The core of the service is to facilitate conversations between logins. Conversational activity is delivered to clients using _events_. Each event notifies interested clients of activity sent to the service through its API.
+The core of the service is to facilitate conversations between users. Conversational activity is delivered to clients using
+_events_. Each event notifies interested clients of activity sent to the service through its API.
## Asynchronous completion
-A number of endpoints return `202 Accepted` responses. The actions performed by those endpoints will be completed before events are delivered. To await the completion of an operation which returns this response, clients must monitor the event stream for the corresponding event.
+A number of endpoints return
+`202 Accepted` responses. The actions performed by those endpoints will be completed before events are delivered. To await the completion of an operation which returns this response, clients must monitor the event stream for the corresponding event.
## `GET /api/events`
@@ -46,11 +48,14 @@ This endpoint is designed for use with the [EventSource] DOM API, and supports s
### Query parameters
-This endpoint requires a `resume_point` (integer) query parameter. The event stream will collect events published after that point in time. The value must be obtained by calling the [`GET /api/boot`](./boot.md) method.
+This endpoint requires a
+`resume_point` (integer) query parameter. The event stream will collect events published after that point in time. The value must be obtained by calling the [
+`GET /api/boot`](./boot.md) method.
### Request headers
-This endpoint accepts an optional `last-event-id` (string) header. If present, the value must be the value of the `id` field of the last message processed by the client. The returned event stream will begin with the following message. If absent, the returned event stream will begin from the start of the event collection.
+This endpoint accepts an optional `last-event-id` (string) header. If present, the value must be the value of the
+`id` field of the last message processed by the client. The returned event stream will begin with the following message. If absent, the returned event stream will begin from the start of the event collection.
This header is set automatically by `EventSource` when reconnecting to an event stream.
@@ -67,7 +72,7 @@ data: "type": "message",
data: "event": "sent",
data: "at": "2024-09-27T23:19:10.208147Z",
data: "channel": "C9876cyyz",
-data: "sender": "L1234abcd",
+data: "sender": "U1234abcd",
data: "id": "M1312acab",
data: "body": "beep"
data: }
@@ -75,46 +80,47 @@ data: }
The service will keep the connection open, and will deliver events as they occur.
-The service may terminate the connection at any time. Clients should reconnect and resume the stream, using the `last-event-id` header to resume from the last message received. The `id` of each event is an ephemeral ID, useful only for this purpose.
+The service may terminate the connection at any time. Clients should reconnect and resume the stream, using the
+`last-event-id` header to resume from the last message received. The
+`id` of each event is an ephemeral ID, useful only for this purpose.
Each event's `data` consists of a JSON object describing one event. Every event includes the following fields:
-| Field | Type | Description |
-|:--------|:-------|:--|
-| `type` | string | The type of entity the event describes. Will be one of the types listed in the next section. |
+| Field | Type | Description |
+|:--------|:-------|:-------------------------------------------------------------------------------------------------------------|
+| `type` | string | The type of entity the event describes. Will be one of the types listed in the next section. |
| `event` | string | The specific kind of event. Will be one of the events listed with the associated `type` in the next section. |
The remaining fields depend on the `type` and `event` field.
-## Login events
+## User events
-The following events describe changes to logins.
+The following events describe changes to users.
-These events have the `type` field set to `"login"`.
+These events have the `type` field set to `"user"`.
-### Login created
+### User created
```json
{
- "type": "login",
- "event": "created",
- "at": "2024-09-27T23:17:10.208147Z",
- "id": "L1234abcd",
- "name": "example username"
+ "type": "user",
+ "event": "created",
+ "at": "2024-09-27T23:17:10.208147Z",
+ "id": "U1234abcd",
+ "name": "example username"
}
```
-Sent whenever a new login is created.
+Sent whenever a new user is created.
These events have the `event` field set to `"created"`. They include the following additional fields:
-| Field | Type | Description |
-|:-------|:----------|:--|
-| `at` | timestamp | The moment the login was created. |
-| `id` | string | A unique identifier for the newly-created login. This can be used to associate the login with other events, or to make API calls targeting the login. |
-| `name` | string | The login's name. |
-
+| Field | Type | Description |
+|:-------|:----------|:---------------------------------------------------------------------------------------------------------------------------------------------------|
+| `at` | timestamp | The moment the user was created. |
+| `id` | string | A unique identifier for the newly-created user. This can be used to associate the user with other events, or to make API calls targeting the user. |
+| `name` | string | The user's name. |
## Channel events
@@ -126,11 +132,11 @@ These events have the `type` field set to `"channel"`.
```json
{
- "type": "channel",
- "event": "created",
- "at": "2024-09-27T23:18:10.208147Z",
- "id": "C9876cyyz",
- "name": "example channel 2"
+ "type": "channel",
+ "event": "created",
+ "at": "2024-09-27T23:18:10.208147Z",
+ "id": "C9876cyyz",
+ "name": "example channel 2"
}
```
@@ -138,14 +144,18 @@ Sent whenever a new channel is created.
These events have the `event` field set to `"created"`. They include the following additional fields:
-| Field | Type | Description |
-|:-------------|:--------------------|:--|
-| `at` | timestamp | The moment the channel was created. |
+| Field | Type | Description |
+|:-------------|:--------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `at` | timestamp | The moment the channel was created. |
| `id` | string | A unique identifier for the newly-created channel. This can be used to associate the channel with other events, or to make API calls targeting the channel. |
-| `name` | string | The channel's name. |
-| `deleted_at` | timestamp, optional | If set, the moment the channel was deleted. |
+| `name` | string | The channel's name. |
+| `deleted_at` | timestamp, optional | If set, the moment the channel was deleted. |
-When a channel is deleted or expires, the `"created"` event is replaced with a tombstone `"created"` event, so that the original channel cannot be trivially recovered from the event stream. Tombstone events have a `deleted_at` field, and a `name` of `""`. Tombstone events for channels use an empty string as the name, and not `null` or with the `name` field removed entirely, to simplify client development. While clients _should_ treat deleted channels specially, for example by rendering them as "channel deleted" markers, they don't have to be - they make sense if interpreted as channels with empty names, too.
+When a channel is deleted or expires, the `"created"` event is replaced with a tombstone
+`"created"` event, so that the original channel cannot be trivially recovered from the event stream. Tombstone events have a
+`deleted_at` field, and a `name` of `""`. Tombstone events for channels use an empty string as the name, and not
+`null` or with the `name` field removed entirely, to simplify client development. While clients
+_should_ treat deleted channels specially, for example by rendering them as "channel deleted" markers, they don't have to be - they make sense if interpreted as channels with empty names, too.
Once a deleted channel is [purged](./channels-messages.md#expiry-and-purging), these tombstone events are removed from the event stream.
@@ -153,10 +163,10 @@ Once a deleted channel is [purged](./channels-messages.md#expiry-and-purging), t
```json
{
- "type": "channel",
- "event": "deleted",
- "at": "2024-09-28T03:40:25.384318Z",
- "id": "C9876cyyz"
+ "type": "channel",
+ "event": "deleted",
+ "at": "2024-09-28T03:40:25.384318Z",
+ "id": "C9876cyyz"
}
```
@@ -164,11 +174,10 @@ Sent whenever a channel is deleted or expires.
These events have the `event` field set to `"deleted"`. They include the following additional fields:
-| Field | Type | Description |
-|:------|:----------|:--|
+| Field | Type | Description |
+|:------|:----------|:------------------------------------|
| `at` | timestamp | The moment the channel was deleted. |
-| `id` | string | The deleted channel's ID. |
-
+| `id` | string | The deleted channel's ID. |
## Message events
@@ -180,13 +189,13 @@ These events have the `type` field set to `"message"`.
```json
{
- "type": "message",
- "event": "sent",
- "at": "2024-09-27T23:19:10.208147Z",
- "channel": "C9876cyyz",
- "sender": "L1234abcd",
- "id": "M1312acab",
- "body": "an effusive blob of prose, condensed down to a single string"
+ "type": "message",
+ "event": "sent",
+ "at": "2024-09-27T23:19:10.208147Z",
+ "channel": "C9876cyyz",
+ "sender": "U1234abcd",
+ "id": "M1312acab",
+ "body": "an effusive blob of prose, condensed down to a single string"
}
```
@@ -194,16 +203,20 @@ Sent whenever a message is sent by a client.
These events have the `event` field set to `"sent"`. They include the following additional fields:
-| Field | Type | Description |
-|:-------------|:--------------------|:--|
-| `at` | timestamp | The moment the message was sent. |
-| `channel` | string | The ID of the channel the message was sent to. |
-| `sender` | string | The ID of the login that sent the message. |
+| Field | Type | Description |
+|:-------------|:--------------------|:----------------------------------------------------------------------------------------------------------------------------------------------|
+| `at` | timestamp | The moment the message was sent. |
+| `channel` | string | The ID of the channel the message was sent to. |
+| `sender` | string | The ID of the user that sent the message. |
| `id` | string | A unique identifier for the message. This can be used to associate the message with other events, or to make API calls targeting the message. |
-| `body` | string | The text of the message. |
-| `deleted_at` | timestamp, optional | If set, the moment the message was deleted. |
+| `body` | string | The text of the message. |
+| `deleted_at` | timestamp, optional | If set, the moment the message was deleted. |
-When a message is deleted or expires, the `"sent"` event is replaced with a tombstone `"sent"` event, so that the original message cannot be trivially recovered from the event stream. Tombstone events have a `deleted_at` field, and a `body` of `""`. Tombstone events for messages use an empty string as the `body`, and not `null` or with the `body` field removed entirely, to simplify client development. While clients _should_ treat deleted messages specially, for example by rendering them as "message deleted" markers, they don't have to be - they make sense if interpreted as messages with empty bodies, too.
+When a message is deleted or expires, the `"sent"` event is replaced with a tombstone
+`"sent"` event, so that the original message cannot be trivially recovered from the event stream. Tombstone events have a
+`deleted_at` field, and a `body` of `""`. Tombstone events for messages use an empty string as the `body`, and not
+`null` or with the `body` field removed entirely, to simplify client development. While clients
+_should_ treat deleted messages specially, for example by rendering them as "message deleted" markers, they don't have to be - they make sense if interpreted as messages with empty bodies, too.
Once a deleted message is [purged](./channels-messages.md#expiry-and-purging), these tombstone events are removed from the event stream.
@@ -211,10 +224,10 @@ Once a deleted message is [purged](./channels-messages.md#expiry-and-purging), t
```json
{
- "type": "message",
- "event": "deleted",
- "at": "2024-09-28T02:44:27.077355Z",
- "id": "M1312acab"
+ "type": "message",
+ "event": "deleted",
+ "at": "2024-09-28T02:44:27.077355Z",
+ "id": "M1312acab"
}
```
@@ -222,7 +235,7 @@ Sent whenever a message is deleted or expires.
These events have the `event` field set to `"deleted"`. They include the following additional fields:
-| Field | Type | Description |
-|:------|:----------|:--|
+| Field | Type | Description |
+|:------|:----------|:------------------------------------|
| `at` | timestamp | The moment the message was deleted. |
-| `id` | string | The deleted message's ID. |
+| `id` | string | The deleted message's ID. |
diff --git a/docs/api/initial-setup.md b/docs/api/initial-setup.md
index c2bdaec..0179397 100644
--- a/docs/api/initial-setup.md
+++ b/docs/api/initial-setup.md
@@ -18,7 +18,8 @@ New instances of this service require an initial setup step before they can full
## Requests before setup completed
-Before the service is set up, all API endpoints, other than those specifically documented as exceptions, will return a status of `503 Service Unavailable` to all requests.
+Before the service is set up, all API endpoints, other than those specifically documented as exceptions, will return a status of
+`503 Service Unavailable` to all requests.
Initial setup can be completed only once.
@@ -27,9 +28,9 @@ Initial setup can be completed only once.
Initial setup performs the following tasks:
-* Create the first login for the service.
+* Create the first user for the service.
- This is the only login that does not require an [invitation](./invitations.md).
+ This is the only user that does not require an [invitation](./invitations.md).
**This endpoint does not require an `identity` cookie.**
@@ -39,16 +40,16 @@ Initial setup performs the following tasks:
```json
{
- "name": "example username",
- "password": "the plaintext password",
+ "name": "example username",
+ "password": "the plaintext password"
}
```
The request must have the following fields:
-| Field | Type | Description |
-|:-----------|:-------|:--|
-| `name` | string | The initial login's name. |
+| Field | Type | Description |
+|:-----------|:-------|:---------------------------------------------|
+| `name` | string | The initial login's name. |
| `password` | string | The initial login's password, in plain text. |
<!-- Reproduced in invitations.md. Edit in both places. -->
@@ -65,27 +66,29 @@ The proposed `name` must be valid. The precise definition of valid is still up i
<!-- This prose is duplicated from authentication.md, with small changes for context. If you edit it here, edit it there, too. -->
-This endpoint will respond with a status of `200 Okay` when successful. The body of the response will be a JSON object describing the newly-created login:
+This endpoint will respond with a status of
+`200 Okay` when successful. The body of the response will be a JSON object describing the newly-created user:
```json
{
- "id": "Labcd1234",
- "name": "Andrea"
+ "id": "Uabcd1234",
+ "name": "Andrea"
}
```
The response will include the following fields:
-| Field | Type | Description |
-|:------------|:-------|:--|
-| `id` | string | A unique identifier for the newly-created login. This can be used to associate the login with other events, or to make API calls targeting the login. |
-| `name` | string | The login's name. |
+| Field | Type | Description |
+|:-------|:-------|:---------------------------------------------------------------------------------------------------------------------------------------------------|
+| `id` | string | A unique identifier for the newly-created user. This can be used to associate the user with other events, or to make API calls targeting the user. |
+| `name` | string | The user's name. |
The returned name may not be identical to the name requested, as the name will be converted to [normalization form C](http://www.unicode.org/reports/tr15/) automatically. The returned name will include this normalization; the service will use the normalized name elsewhere, and does not store the originally requested name.
The provided password will also be converted to normalization form C. However, the normalized password is not returned to the client.
-The response will include a `Set-Cookie` header for the `identity` cookie, providing the client with a newly-minted identity token associated with the initial login created for this request. See the [authentication](./authentication) section for details on how this cookie may be used.
+The response will include a `Set-Cookie` header for the
+`identity` cookie, providing the client with a newly-minted identity token associated with the initial user created for this request. See the [authentication](./authentication) section for details on how this cookie may be used.
The cookie will expire if it is not used regularly.
@@ -95,5 +98,6 @@ This endpoint will respond with a status of `400 Bad Request` if the proposed `n
### Setup previously completed
-Once completed, this operation cannot be performed a second time. Subsequent requests to this endpoint will respond with a status of `409 Conflict`.
+Once completed, this operation cannot be performed a second time. Subsequent requests to this endpoint will respond with a status of
+`409 Conflict`.
diff --git a/docs/api/invitations.md b/docs/api/invitations.md
index 1839ef5..0be2c2e 100644
--- a/docs/api/invitations.md
+++ b/docs/api/invitations.md
@@ -21,9 +21,9 @@ sequenceDiagram
API -->>- Blake : Success
```
-Other than the login created during [initial setup](./initial-setup.md), new logins can only be created by using invitations.
+Other than the user created during [initial setup](./initial-setup.md), new users can only be created by using invitations.
-Any login can create invitations. Each invitation can be accepted at most once. An invitation which is not accepted within 24 hours expires.
+Any user can create invitations. Each invitation can be accepted at most once. An invitation which is not accepted within 24 hours expires.
## `POST /api/invite`
@@ -40,25 +40,27 @@ The request must be an empty JSON object.
### Success
-This endpoint will respond with a status of `200 Okay` when successful. The body of the response will be a JSON object describing the new invitation:
+This endpoint will respond with a status of
+`200 Okay` when successful. The body of the response will be a JSON object describing the new invitation:
```json
{
- "id": "I3884",
- "issuer": "Labcd1234",
- "issued_at": "2024-10-12T01:43:12.001853Z"
+ "id": "I3884",
+ "issuer": "Uabcd1234",
+ "issued_at": "2024-10-12T01:43:12.001853Z"
}
```
The response will include the following fields:
-| Field | Type | Description |
-|:------------|:-------|:--|
+| Field | Type | Description |
+|:------------|:-------|:------------------------------------------------------------------------------|
| `id` | string | A unique identifier for the invitation. This ID must be given to the invitee. |
-| `issuer` | string | The login ID of the invitation's issuer. |
-| `issued_at` | string | The timestamp from which the invitation will expire. |
+| `issuer` | string | The user ID of the invitation's issuer. |
+| `issued_at` | string | The timestamp from which the invitation will expire. |
-Clients and their operators are responsible for delivering the invitation to the invitee. Clients are strongly recommended to construct a URL for the invitation so that the invitee can take action on it easily. The included client supports URLs of the format `https://example.net/invite/:id` (with the `:id` placeholder substituted with the invitation's ID).
+Clients and their operators are responsible for delivering the invitation to the invitee. Clients are strongly recommended to construct a URL for the invitation so that the invitee can take action on it easily. The included client supports URLs of the format
+`https://example.net/invite/:id` (with the `:id` placeholder substituted with the invitation's ID).
## `GET /api/invite/:id`
@@ -67,50 +69,53 @@ Returns information about an outstanding invitation.
This endpoint requires the following path parameter:
-| Parameter | Type | Description |
-|:----------|:-------|:--|
-| `id` | string | An invitation ID, as returned from a previous request to `POST /api/invite`. |
+| Parameter | Type | Description |
+|:----------|:-------|:-----------------------------------------------------------------------------|
+| `id` | string | An invitation ID, as returned from a previous request to `POST /api/invite`. |
**This endpoint does not require an `identity` cookie.**
### On success
-This endpoint will respond with a status of `200 Okay` when successful. The body of the response will be a JSON object describing the invitation:
+This endpoint will respond with a status of
+`200 Okay` when successful. The body of the response will be a JSON object describing the invitation:
```json
{
- "issuer": {
- "id": "Labcd1234",
- "name": "i send you invites"
- },
- "issued_at": "2024-10-12T01:43:12.001853Z"
+ "issuer": {
+ "id": "Uabcd1234",
+ "name": "i send you invites"
+ },
+ "issued_at": "2024-10-12T01:43:12.001853Z"
}
```
The response will include the following fields:
-| Field | Type | Description |
-|:------------|:-------|:--|
-| `id` | string | The ID of the invitation. |
-| `issuer` | string | The login name of the invitation's issuer. |
+| Field | Type | Description |
+|:------------|:-------|:-----------------------------------------------------|
+| `id` | string | The ID of the invitation. |
+| `issuer` | string | The name of the invitation's issuer. |
| `issued_at` | string | The timestamp from which the invitation will expire. |
-Clients should present the `issuer` to the user when presenting an invitation, so as to personalize the invitation and help them understand their connection with the service.
+Clients should present the
+`issuer` to the user when presenting an invitation, so as to personalize the invitation and help them understand their connection with the service.
### Invitation not found
-This endpoint will respond with a status of `404 Not Found` when the invitation ID either does not exist, or has already been accepted.
+This endpoint will respond with a status of
+`404 Not Found` when the invitation ID either does not exist, or has already been accepted.
## `POST /api/invite/:id`
-Accepts an invitation and creates a new login.
+Accepts an invitation and creates a new user.
This endpoint requires the following path parameter:
-| Parameter | Type | Description |
-|:----------|:-------|:--|
-| `id` | string | An invitation ID, as returned from a previous request to `POST /api/invite`. |
+| Parameter | Type | Description |
+|:----------|:-------|:-----------------------------------------------------------------------------|
+| `id` | string | An invitation ID, as returned from a previous request to `POST /api/invite`. |
**This endpoint does not require an `identity` cookie.**
@@ -118,17 +123,17 @@ This endpoint requires the following path parameter:
```json
{
- "name": "example login",
- "password": "correct-horse-battery-staple"
+ "name": "example user",
+ "password": "correct-horse-battery-staple"
}
```
The request must have the following fields:
-| Field | Type | Description |
-|:-----------|:-------|:--|
-| `name` | string | The new login's name. |
-| `password` | string | The new login's password, in plain text. |
+| Field | Type | Description |
+|:-----------|:-------|:----------------------------------------|
+| `name` | string | The new user's name. |
+| `password` | string | The new user's password, in plain text. |
<!-- Reproduced in initial-setup.md. Edit in both places. -->
The proposed `name` must be valid. The precise definition of valid is still up in the air, but, at minimum:
@@ -143,40 +148,45 @@ The proposed `name` must be valid. The precise definition of valid is still up i
<!-- This prose is duplicated from authentication.md, with small changes for context. If you edit it here, edit it there, too. -->
-This endpoint will respond with a status of `200 Okay` when successful. The body of the response will be a JSON object describing the newly-created login:
+This endpoint will respond with a status of
+`200 Okay` when successful. The body of the response will be a JSON object describing the newly-created user:
```json
{
- "id": "Labcd1234",
- "name": "Andrea"
+ "id": "Uabcd1234",
+ "name": "Andrea"
}
```
The response will include the following fields:
-| Field | Type | Description |
-|:------------|:-------|:--|
-| `id` | string | A unique identifier for the newly-created login. This can be used to associate the login with other events, or to make API calls targeting the login. |
-| `name` | string | The login's name. |
+| Field | Type | Description |
+|:-------|:-------|:---------------------------------------------------------------------------------------------------------------------------------------------------|
+| `id` | string | A unique identifier for the newly-created user. This can be used to associate the user with other events, or to make API calls targeting the user. |
+| `name` | string | The user's name. |
The returned name may not be identical to the name requested, as the name will be converted to [normalization form C](http://www.unicode.org/reports/tr15/) automatically. The returned name will include this normalization; the service will use the normalized name elsewhere, and does not store the originally requested name.
The provided password will also be converted to normalization form C. However, the normalized password is not returned to the client.
-The response will include a `Set-Cookie` header for the `identity` cookie, providing the client with a newly-minted identity token associated with the login created for this request. See the [authentication](./authentication.md) section for details on how this cookie may be used.
+The response will include a `Set-Cookie` header for the
+`identity` cookie, providing the client with a newly-minted identity token associated with the login created for this request. See the [authentication](./authentication.md) section for details on how this cookie may be used.
The cookie will expire if it is not used regularly.
### Invitation not found
-This endpoint will respond with a status of `404 Not Found` when the invitation ID either does not exist, or has already been accepted.
+This endpoint will respond with a status of
+`404 Not Found` when the invitation ID either does not exist, or has already been accepted.
### Name not valid
This endpoint will respond with a status of `400 Bad Request` if the proposed `name` is not valid.
+The invitation can be accepted with a different name.
+
### Name in use
-This endpoint will respond with a status of `409 Conflict` if the requested login name has already been taken.
+This endpoint will respond with a status of `409 Conflict` if the requested `name` has already been taken.
The invitation can be accepted with a different name.
diff --git a/migrations/20250323190045_rename_login_to_user.sql b/migrations/20250323190045_rename_login_to_user.sql
new file mode 100644
index 0000000..7b5861d
--- /dev/null
+++ b/migrations/20250323190045_rename_login_to_user.sql
@@ -0,0 +1,151 @@
+-- message
+-- message_deleted
+
+-- Set up the new `user` table, and copy existing logins into it
+create table user
+(
+ id text
+ not null
+ primary key,
+ display_name text
+ not null,
+ canonical_name text
+ not null
+ unique,
+ password_hash text
+ not null,
+ created_sequence bigint
+ unique
+ not null,
+ created_at text
+ not null
+);
+
+insert into user (id, display_name, canonical_name, password_hash, created_sequence, created_at)
+select id, display_name, canonical_name, password_hash, created_sequence, created_at
+from login;
+
+-- Carry the rename through `token`, preserving data…
+alter table token
+ rename to old_token;
+
+create table token
+(
+ id text
+ not null
+ primary key,
+ secret text
+ not null
+ unique,
+ user text
+ not null,
+ issued_at text
+ not null,
+ last_used_at text
+ not null,
+ foreign key (user)
+ references user (id)
+);
+
+insert into token (id, secret, user, issued_at, last_used_at)
+select id, secret, login, issued_at, last_used_at
+from old_token;
+
+-- Carry the rename through `invite`, preserving data…
+alter table invite
+ rename to old_invite;
+
+create table invite
+(
+ id text
+ primary key
+ not null,
+ issuer text
+ not null
+ references user (id),
+ issued_at text
+ not null
+);
+
+insert into invite (id, issuer, issued_at)
+select id, issuer, issued_at
+from old_invite;
+
+-- Carry the rename through `message`, preserving data…
+alter table message
+ rename to old_message;
+
+create table message
+(
+ id text
+ not null
+ primary key,
+ channel text
+ not null
+ references channel (id),
+ sender text
+ not null
+ references user (id),
+ sent_sequence bigint
+ unique
+ not null,
+ sent_at text
+ not null,
+ body text
+ null,
+ last_sequence bigint
+ not null
+);
+
+insert into message (id, channel, sender, sent_sequence, sent_at, body, last_sequence)
+select id, channel, sender, sent_sequence, sent_at, body, last_sequence
+from old_message;
+
+-- Recreating `message` entails recreating `message_deleted`
+alter table message_deleted
+ rename to old_message_deleted;
+
+create table message_deleted
+(
+ id text
+ not null
+ primary key
+ references message (id),
+ deleted_sequence bigint
+ unique
+ not null,
+ deleted_at text
+ not null
+);
+
+insert into message_deleted (id, deleted_sequence, deleted_at)
+select id, deleted_sequence, deleted_at
+from old_message_deleted;
+
+-- Delete old tables (which will take old indices with them)
+drop table old_message_deleted;
+drop table old_message;
+drop table old_invite;
+drop table old_token;
+drop table login;
+
+-- Recreate indices
+create index message_deleted_deleted_at
+ on message_deleted (deleted_at);
+
+create index message_sent_at
+ on message (sent_at);
+create index message_channel
+ on message (channel);
+create index message_last_sequence
+ on message (last_sequence);
+
+create index invite_issued_at
+ on invite (issued_at);
+
+create index token_issued_at
+ on token (issued_at);
+create index token_last_used_at
+ on token (last_used_at);
+create index token_user
+ on token (user);
diff --git a/src/app.rs b/src/app.rs
index 0dbf017..b7e52a4 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -11,7 +11,7 @@ use crate::{
};
#[cfg(test)]
-use crate::login::app::Logins;
+use crate::user::app::Users;
#[derive(Clone)]
pub struct App {
@@ -50,8 +50,8 @@ impl App {
}
#[cfg(test)]
- pub const fn logins(&self) -> Logins {
- Logins::new(&self.db, &self.events)
+ pub const fn users(&self) -> Users {
+ Users::new(&self.db, &self.events)
}
pub const fn messages(&self) -> Messages {
diff --git a/src/boot/app.rs b/src/boot/app.rs
index 909f7d8..f531afe 100644
--- a/src/boot/app.rs
+++ b/src/boot/app.rs
@@ -4,9 +4,9 @@ use super::Snapshot;
use crate::{
channel::{self, repo::Provider as _},
event::repo::Provider as _,
- login::{self, repo::Provider as _},
message::repo::Provider as _,
name,
+ user::{self, repo::Provider as _},
};
pub struct Boot<'a> {
@@ -22,15 +22,15 @@ impl<'a> Boot<'a> {
let mut tx = self.db.begin().await?;
let resume_point = tx.sequence().current().await?;
- let logins = tx.logins().all(resume_point).await?;
+ let users = tx.users().all(resume_point).await?;
let channels = tx.channels().all(resume_point).await?;
let messages = tx.messages().all(resume_point).await?;
tx.commit().await?;
- let logins = logins
+ let users = users
.into_iter()
- .filter_map(|login| login.as_of(resume_point))
+ .filter_map(|user| user.as_of(resume_point))
.collect();
let channels = channels
@@ -45,7 +45,7 @@ impl<'a> Boot<'a> {
Ok(Snapshot {
resume_point,
- logins,
+ users,
channels,
messages,
})
@@ -59,9 +59,9 @@ pub enum Error {
Database(#[from] sqlx::Error),
}
-impl From<login::repo::LoadError> for Error {
- fn from(error: login::repo::LoadError) -> Self {
- use login::repo::LoadError;
+impl From<user::repo::LoadError> for Error {
+ fn from(error: user::repo::LoadError) -> Self {
+ use user::repo::LoadError;
match error {
LoadError::Name(error) => error.into(),
LoadError::Database(error) => error.into(),
diff --git a/src/boot/mod.rs b/src/boot/mod.rs
index ed4764a..c52b088 100644
--- a/src/boot/mod.rs
+++ b/src/boot/mod.rs
@@ -1,14 +1,14 @@
pub mod app;
mod routes;
-use crate::{channel::Channel, event::Sequence, login::Login, message::Message};
+use crate::{channel::Channel, event::Sequence, message::Message, user::User};
pub use self::routes::router;
#[derive(serde::Serialize)]
pub struct Snapshot {
pub resume_point: Sequence,
- pub logins: Vec<Login>,
+ pub users: Vec<User>,
pub channels: Vec<Channel>,
pub messages: Vec<Message>,
}
diff --git a/src/boot/routes/get.rs b/src/boot/routes/get.rs
index 563fbf1..4873b7a 100644
--- a/src/boot/routes/get.rs
+++ b/src/boot/routes/get.rs
@@ -3,19 +3,19 @@ use axum::{
response::{self, IntoResponse},
};
-use crate::{app::App, boot::Snapshot, error::Internal, login::Login, token::extract::Identity};
+use crate::{app::App, boot::Snapshot, error::Internal, token::extract::Identity, user::User};
pub async fn handler(State(app): State<App>, identity: Identity) -> Result<Response, Internal> {
let snapshot = app.boot().snapshot().await?;
Ok(Response {
- login: identity.login,
+ user: identity.user,
snapshot,
})
}
#[derive(serde::Serialize)]
pub struct Response {
- pub login: Login,
+ pub user: User,
#[serde(flatten)]
pub snapshot: Snapshot,
}
diff --git a/src/boot/routes/test.rs b/src/boot/routes/test.rs
index 202dcb9..55802fe 100644
--- a/src/boot/routes/test.rs
+++ b/src/boot/routes/test.rs
@@ -12,20 +12,20 @@ async fn returns_identity() {
.await
.expect("boot always succeeds");
- assert_eq!(viewer.login, response.login);
+ assert_eq!(viewer.user, response.user);
}
#[tokio::test]
async fn includes_logins() {
let app = fixtures::scratch_app().await;
- let spectator = fixtures::login::create(&app, &fixtures::now()).await;
+ let spectator = fixtures::user::create(&app, &fixtures::now()).await;
let viewer = fixtures::identity::fictitious();
let response = get::handler(State(app), viewer)
.await
.expect("boot always succeeds");
- assert!(response.snapshot.logins.contains(&spectator));
+ assert!(response.snapshot.users.contains(&spectator));
}
#[tokio::test]
@@ -44,7 +44,7 @@ async fn includes_channels() {
#[tokio::test]
async fn includes_messages() {
let app = fixtures::scratch_app().await;
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let channel = fixtures::channel::create(&app, &fixtures::now()).await;
let message = fixtures::message::send(&app, &channel, &sender, &fixtures::now()).await;
@@ -59,7 +59,7 @@ async fn includes_messages() {
#[tokio::test]
async fn excludes_expired_messages() {
let app = fixtures::scratch_app().await;
- let sender = fixtures::login::create(&app, &fixtures::ancient()).await;
+ let sender = fixtures::user::create(&app, &fixtures::ancient()).await;
let channel = fixtures::channel::create(&app, &fixtures::ancient()).await;
let expired_message =
fixtures::message::send(&app, &channel, &sender, &fixtures::ancient()).await;
@@ -80,7 +80,7 @@ async fn excludes_expired_messages() {
#[tokio::test]
async fn excludes_deleted_messages() {
let app = fixtures::scratch_app().await;
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let channel = fixtures::channel::create(&app, &fixtures::now()).await;
let deleted_message = fixtures::message::send(&app, &channel, &sender, &fixtures::now()).await;
diff --git a/src/channel/routes/channel/post.rs b/src/channel/routes/channel/post.rs
index 3f14d64..0aad5e5 100644
--- a/src/channel/routes/channel/post.rs
+++ b/src/channel/routes/channel/post.rs
@@ -21,7 +21,7 @@ pub async fn handler(
) -> Result<Response, Error> {
let message = app
.messages()
- .send(&channel, &identity.login, &sent_at, &request.body)
+ .send(&channel, &identity.user, &sent_at, &request.body)
.await?;
Ok(Response(message))
diff --git a/src/channel/routes/channel/test/delete.rs b/src/channel/routes/channel/test/delete.rs
index 77a0b03..bd9261d 100644
--- a/src/channel/routes/channel/test/delete.rs
+++ b/src/channel/routes/channel/test/delete.rs
@@ -156,7 +156,7 @@ pub async fn channel_not_empty() {
let app = fixtures::scratch_app().await;
let channel = fixtures::channel::create(&app, &fixtures::now()).await;
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
fixtures::message::send(&app, &channel, &sender, &fixtures::now()).await;
// Send the request
diff --git a/src/channel/routes/channel/test/post.rs b/src/channel/routes/channel/test/post.rs
index bc0684b..d9527ac 100644
--- a/src/channel/routes/channel/test/post.rs
+++ b/src/channel/routes/channel/test/post.rs
@@ -55,7 +55,7 @@ async fn messages_in_order() {
.await
{
assert_eq!(*sent_at, event.at());
- assert_eq!(sender.login.id, event.message.sender);
+ assert_eq!(sender.user.id, event.message.sender);
assert_eq!(body, event.message.body);
}
}
diff --git a/src/cli.rs b/src/cli.rs
index 775df7f..4232c00 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -17,9 +17,9 @@ use tokio::net;
use crate::{
app::App,
- boot, channel, clock, db, event, expire, invite, login, message,
+ boot, channel, clock, db, event, expire, invite, message,
setup::{self, middleware::setup_required},
- ui,
+ ui, user,
};
/// Command-line entry point for running the `pilcrow` server.
@@ -136,7 +136,7 @@ fn routers(app: &App) -> Router<App> {
channel::router(),
event::router(),
invite::router(),
- login::router(),
+ user::router(),
message::router(),
]
.into_iter()
diff --git a/src/event/app.rs b/src/event/app.rs
index 8661c90..45a9099 100644
--- a/src/event/app.rs
+++ b/src/event/app.rs
@@ -8,9 +8,9 @@ use sqlx::sqlite::SqlitePool;
use super::{Event, Sequence, Sequenced, broadcaster::Broadcaster};
use crate::{
channel::{self, repo::Provider as _},
- login::{self, repo::Provider as _},
message::{self, repo::Provider as _},
name,
+ user::{self, repo::Provider as _},
};
pub struct Events<'a> {
@@ -33,10 +33,10 @@ impl<'a> Events<'a> {
let mut tx = self.db.begin().await?;
- let logins = tx.logins().replay(resume_at).await?;
- let login_events = logins
+ let users = tx.users().replay(resume_at).await?;
+ let user_events = users
.iter()
- .map(login::History::events)
+ .map(user::History::events)
.kmerge_by(Sequence::merge)
.filter(Sequence::after(resume_at))
.map(Event::from);
@@ -57,7 +57,7 @@ impl<'a> Events<'a> {
.filter(Sequence::after(resume_at))
.map(Event::from);
- let replay_events = login_events
+ let replay_events = user_events
.merge_by(channel_events, Sequence::merge)
.merge_by(message_events, Sequence::merge)
.collect::<Vec<_>>();
@@ -88,9 +88,9 @@ pub enum Error {
Name(#[from] name::Error),
}
-impl From<login::repo::LoadError> for Error {
- fn from(error: login::repo::LoadError) -> Self {
- use login::repo::LoadError;
+impl From<user::repo::LoadError> for Error {
+ fn from(error: user::repo::LoadError) -> Self {
+ use user::repo::LoadError;
match error {
LoadError::Database(error) => error.into(),
LoadError::Name(error) => error.into(),
diff --git a/src/event/mod.rs b/src/event/mod.rs
index 9996916..3ab88ec 100644
--- a/src/event/mod.rs
+++ b/src/event/mod.rs
@@ -1,4 +1,4 @@
-use crate::{channel, login, message};
+use crate::{channel, message, user};
pub mod app;
mod broadcaster;
@@ -16,7 +16,7 @@ pub use self::{
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Event {
- Login(login::Event),
+ User(user::Event),
Channel(channel::Event),
Message(message::Event),
}
@@ -24,16 +24,16 @@ pub enum Event {
impl Sequenced for Event {
fn instant(&self) -> Instant {
match self {
- Self::Login(event) => event.instant(),
+ Self::User(event) => event.instant(),
Self::Channel(event) => event.instant(),
Self::Message(event) => event.instant(),
}
}
}
-impl From<login::Event> for Event {
- fn from(event: login::Event) -> Self {
- Self::Login(event)
+impl From<user::Event> for Event {
+ fn from(event: user::Event) -> Self {
+ Self::User(event)
}
}
diff --git a/src/event/routes/test/invite.rs b/src/event/routes/test/invite.rs
index 73af62d..1d1bec6 100644
--- a/src/event/routes/test/invite.rs
+++ b/src/event/routes/test/invite.rs
@@ -12,7 +12,7 @@ async fn accepting_invite() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let issuer = fixtures::login::create(&app, &fixtures::now()).await;
+ let issuer = fixtures::user::create(&app, &fixtures::now()).await;
let invite = fixtures::invite::issue(&app, &issuer, &fixtures::now()).await;
let resume_point = fixtures::boot::resume_point(&app).await;
@@ -30,7 +30,7 @@ async fn accepting_invite() {
// Accept the invite
- let (name, password) = fixtures::login::propose();
+ let (name, password) = fixtures::user::propose();
let (joiner, _) = app
.invites()
.accept(&invite.id, &name, &password, &fixtures::now())
@@ -40,9 +40,9 @@ async fn accepting_invite() {
// Expect a login created event
let _ = events
- .filter_map(fixtures::event::login)
- .filter_map(fixtures::event::login::created)
- .filter(|event| future::ready(event.login == joiner))
+ .filter_map(fixtures::event::user)
+ .filter_map(fixtures::event::user::created)
+ .filter(|event| future::ready(event.user == joiner))
.next()
.expect_some("a login created event is sent")
.await;
@@ -53,13 +53,13 @@ async fn previously_accepted_invite() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let issuer = fixtures::login::create(&app, &fixtures::now()).await;
+ let issuer = fixtures::user::create(&app, &fixtures::now()).await;
let invite = fixtures::invite::issue(&app, &issuer, &fixtures::now()).await;
let resume_point = fixtures::boot::resume_point(&app).await;
// Accept the invite
- let (name, password) = fixtures::login::propose();
+ let (name, password) = fixtures::user::propose();
let (joiner, _) = app
.invites()
.accept(&invite.id, &name, &password, &fixtures::now())
@@ -81,9 +81,9 @@ async fn previously_accepted_invite() {
// Expect a login created event
let _ = events
- .filter_map(fixtures::event::login)
- .filter_map(fixtures::event::login::created)
- .filter(|event| future::ready(event.login == joiner))
+ .filter_map(fixtures::event::user)
+ .filter_map(fixtures::event::user::created)
+ .filter(|event| future::ready(event.user == joiner))
.next()
.expect_some("a login created event is sent")
.await;
diff --git a/src/event/routes/test/message.rs b/src/event/routes/test/message.rs
index fafaeb3..84a3aec 100644
--- a/src/event/routes/test/message.rs
+++ b/src/event/routes/test/message.rs
@@ -32,7 +32,7 @@ async fn sending() {
// Send a message
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let message = app
.messages()
.send(
@@ -65,7 +65,7 @@ async fn previously_sent() {
// Send a message
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let message = app
.messages()
.send(
@@ -105,7 +105,7 @@ async fn sent_in_multiple_channels() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let resume_point = fixtures::boot::resume_point(&app).await;
let channels = [
@@ -156,7 +156,7 @@ async fn sent_sequentially() {
let app = fixtures::scratch_app().await;
let channel = fixtures::channel::create(&app, &fixtures::now()).await;
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let resume_point = fixtures::boot::resume_point(&app).await;
let messages = vec![
@@ -200,7 +200,7 @@ async fn expiring() {
let app = fixtures::scratch_app().await;
let channel = fixtures::channel::create(&app, &fixtures::ancient()).await;
- let sender = fixtures::login::create(&app, &fixtures::ancient()).await;
+ let sender = fixtures::user::create(&app, &fixtures::ancient()).await;
let message = fixtures::message::send(&app, &channel, &sender, &fixtures::ancient()).await;
let resume_point = fixtures::boot::resume_point(&app).await;
@@ -239,7 +239,7 @@ async fn previously_expired() {
let app = fixtures::scratch_app().await;
let channel = fixtures::channel::create(&app, &fixtures::ancient()).await;
- let sender = fixtures::login::create(&app, &fixtures::ancient()).await;
+ let sender = fixtures::user::create(&app, &fixtures::ancient()).await;
let message = fixtures::message::send(&app, &channel, &sender, &fixtures::ancient()).await;
let resume_point = fixtures::boot::resume_point(&app).await;
@@ -278,7 +278,7 @@ async fn deleting() {
let app = fixtures::scratch_app().await;
let channel = fixtures::channel::create(&app, &fixtures::now()).await;
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let message = fixtures::message::send(&app, &channel, &sender, &fixtures::now()).await;
let resume_point = fixtures::boot::resume_point(&app).await;
@@ -317,7 +317,7 @@ async fn previously_deleted() {
let app = fixtures::scratch_app().await;
let channel = fixtures::channel::create(&app, &fixtures::now()).await;
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let message = fixtures::message::send(&app, &channel, &sender, &fixtures::now()).await;
let resume_point = fixtures::boot::resume_point(&app).await;
@@ -356,7 +356,7 @@ async fn previously_purged() {
let app = fixtures::scratch_app().await;
let channel = fixtures::channel::create(&app, &fixtures::ancient()).await;
- let sender = fixtures::login::create(&app, &fixtures::ancient()).await;
+ let sender = fixtures::user::create(&app, &fixtures::ancient()).await;
let message = fixtures::message::send(&app, &channel, &sender, &fixtures::ancient()).await;
let resume_point = fixtures::boot::resume_point(&app).await;
diff --git a/src/event/routes/test/resume.rs b/src/event/routes/test/resume.rs
index dc27691..633eae3 100644
--- a/src/event/routes/test/resume.rs
+++ b/src/event/routes/test/resume.rs
@@ -15,7 +15,7 @@ async fn resume() {
let app = fixtures::scratch_app().await;
let channel = fixtures::channel::create(&app, &fixtures::now()).await;
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let resume_point = fixtures::boot::resume_point(&app).await;
let initial_message = fixtures::message::send(&app, &channel, &sender, &fixtures::now()).await;
@@ -96,7 +96,7 @@ async fn serial_resume() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let channel_a = fixtures::channel::create(&app, &fixtures::now()).await;
let channel_b = fixtures::channel::create(&app, &fixtures::now()).await;
let resume_point = fixtures::boot::resume_point(&app).await;
diff --git a/src/event/routes/test/setup.rs b/src/event/routes/test/setup.rs
index 26b7ea7..1170fe4 100644
--- a/src/event/routes/test/setup.rs
+++ b/src/event/routes/test/setup.rs
@@ -19,7 +19,7 @@ async fn previously_completed() {
// Complete initial setup
- let (name, password) = fixtures::login::propose();
+ let (name, password) = fixtures::user::propose();
let (owner, _) = app
.setup()
.initial(&name, &password, &fixtures::now())
@@ -41,9 +41,9 @@ async fn previously_completed() {
// Expect a login created event
let _ = events
- .filter_map(fixtures::event::login)
- .filter_map(fixtures::event::login::created)
- .filter(|event| future::ready(event.login == owner))
+ .filter_map(fixtures::event::user)
+ .filter_map(fixtures::event::user::created)
+ .filter(|event| future::ready(event.user == owner))
.next()
.expect_some("a login created event is sent")
.await;
diff --git a/src/event/routes/test/token.rs b/src/event/routes/test/token.rs
index fa76865..a467de5 100644
--- a/src/event/routes/test/token.rs
+++ b/src/event/routes/test/token.rs
@@ -13,12 +13,12 @@ async fn terminates_on_token_expiry() {
let app = fixtures::scratch_app().await;
let channel = fixtures::channel::create(&app, &fixtures::now()).await;
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let resume_point = fixtures::boot::resume_point(&app).await;
// Subscribe via the endpoint
- let subscriber_creds = fixtures::login::create_with_password(&app, &fixtures::now()).await;
+ let subscriber_creds = fixtures::user::create_with_password(&app, &fixtures::now()).await;
let subscriber =
fixtures::identity::logged_in(&app, &subscriber_creds, &fixtures::ancient()).await;
@@ -60,7 +60,7 @@ async fn terminates_on_logout() {
let app = fixtures::scratch_app().await;
let channel = fixtures::channel::create(&app, &fixtures::now()).await;
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let resume_point = fixtures::boot::resume_point(&app).await;
// Subscribe via the endpoint
@@ -106,12 +106,12 @@ async fn terminates_on_password_change() {
let app = fixtures::scratch_app().await;
let channel = fixtures::channel::create(&app, &fixtures::now()).await;
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let resume_point = fixtures::boot::resume_point(&app).await;
// Subscribe via the endpoint
- let creds = fixtures::login::create_with_password(&app, &fixtures::now()).await;
+ let creds = fixtures::user::create_with_password(&app, &fixtures::now()).await;
let cookie = fixtures::cookie::logged_in(&app, &creds, &fixtures::now()).await;
let subscriber = fixtures::identity::from_cookie(&app, &cookie, &fixtures::now()).await;
@@ -127,9 +127,9 @@ async fn terminates_on_password_change() {
// Verify the resulting stream's behaviour
let (_, password) = creds;
- let to = fixtures::login::propose_password();
+ let to = fixtures::user::propose_password();
app.tokens()
- .change_password(&subscriber.login, &password, &to, &fixtures::now())
+ .change_password(&subscriber.user, &password, &to, &fixtures::now())
.await
.expect("expiring tokens succeeds");
diff --git a/src/invite/app.rs b/src/invite/app.rs
index c56c9b3..e7bd5c6 100644
--- a/src/invite/app.rs
+++ b/src/invite/app.rs
@@ -6,12 +6,12 @@ use crate::{
clock::DateTime,
db::{Duplicate as _, NotFound as _},
event::Broadcaster,
- login::{
- Login, Password,
- create::{self, Create},
- },
name::Name,
token::{Secret, repo::Provider as _},
+ user::{
+ Password, User,
+ create::{self, Create},
+ },
};
pub struct Invites<'a> {
@@ -24,7 +24,7 @@ impl<'a> Invites<'a> {
Self { db, events }
}
- pub async fn issue(&self, issuer: &Login, issued_at: &DateTime) -> Result<Invite, sqlx::Error> {
+ pub async fn issue(&self, issuer: &User, issued_at: &DateTime) -> Result<Invite, sqlx::Error> {
let mut tx = self.db.begin().await?;
let invite = tx.invites().create(issuer, issued_at).await?;
tx.commit().await?;
@@ -46,7 +46,7 @@ impl<'a> Invites<'a> {
name: &Name,
password: &Password,
accepted_at: &DateTime,
- ) -> Result<(Login, Secret), AcceptError> {
+ ) -> Result<(User, Secret), AcceptError> {
let create = Create::begin(name, password, accepted_at);
let mut tx = self.db.begin().await?;
@@ -70,7 +70,7 @@ impl<'a> Invites<'a> {
.store(&mut tx)
.await
.duplicate(|| AcceptError::DuplicateLogin(name.clone()))?;
- let secret = tx.tokens().issue(stored.login(), accepted_at).await?;
+ let secret = tx.tokens().issue(stored.user(), accepted_at).await?;
tx.commit().await?;
let login = stored.publish(self.events);
@@ -94,7 +94,7 @@ impl<'a> Invites<'a> {
pub enum AcceptError {
#[error("invite not found: {0}")]
NotFound(Id),
- #[error("invalid login name: {0}")]
+ #[error("invalid user name: {0}")]
InvalidName(Name),
#[error("name in use: {0}")]
DuplicateLogin(Name),
diff --git a/src/invite/mod.rs b/src/invite/mod.rs
index 53ca984..2d32fda 100644
--- a/src/invite/mod.rs
+++ b/src/invite/mod.rs
@@ -3,14 +3,14 @@ mod id;
mod repo;
mod routes;
-use crate::{clock::DateTime, login, normalize::nfc};
+use crate::{clock::DateTime, normalize::nfc, user};
pub use self::{id::Id, routes::router};
#[derive(Debug, serde::Serialize)]
pub struct Invite {
pub id: Id,
- pub issuer: login::Id,
+ pub issuer: user::Id,
pub issued_at: DateTime,
}
diff --git a/src/invite/repo.rs b/src/invite/repo.rs
index c1dc701..79114ec 100644
--- a/src/invite/repo.rs
+++ b/src/invite/repo.rs
@@ -3,8 +3,8 @@ use sqlx::{SqliteConnection, Transaction, sqlite::Sqlite};
use super::{Id, Invite, Summary};
use crate::{
clock::DateTime,
- login::{self, Login},
normalize::nfc,
+ user::{self, User},
};
pub trait Provider {
@@ -22,7 +22,7 @@ pub struct Invites<'t>(&'t mut SqliteConnection);
impl Invites<'_> {
pub async fn create(
&mut self,
- issuer: &Login,
+ issuer: &User,
issued_at: &DateTime,
) -> Result<Invite, sqlx::Error> {
let id = Id::generate();
@@ -33,7 +33,7 @@ impl Invites<'_> {
values ($1, $2, $3)
returning
id as "id: Id",
- issuer as "issuer: login::Id",
+ issuer as "issuer: user::Id",
issued_at as "issued_at: DateTime"
"#,
id,
@@ -52,7 +52,7 @@ impl Invites<'_> {
r#"
select
id as "id: Id",
- issuer as "issuer: login::Id",
+ issuer as "issuer: user::Id",
issued_at as "issued_at: DateTime"
from invite
where id = $1
@@ -70,11 +70,11 @@ impl Invites<'_> {
r#"
select
invite.id as "invite_id: Id",
- issuer.id as "issuer_id: login::Id",
+ issuer.id as "issuer_id: user::Id",
issuer.display_name as "issuer_name: nfc::String",
invite.issued_at as "invite_issued_at: DateTime"
from invite
- join login as issuer on (invite.issuer = issuer.id)
+ join user as issuer on (invite.issuer = issuer.id)
where invite.id = $1
"#,
invite,
diff --git a/src/invite/routes/invite/post.rs b/src/invite/routes/invite/post.rs
index bb68e07..58d15c2 100644
--- a/src/invite/routes/invite/post.rs
+++ b/src/invite/routes/invite/post.rs
@@ -9,9 +9,9 @@ use crate::{
clock::RequestedAt,
error::{Internal, NotFound},
invite::app,
- login::{Login, Password},
name::Name,
token::extract::IdentityCookie,
+ user::{Password, User},
};
pub async fn handler(
@@ -20,7 +20,7 @@ pub async fn handler(
identity: IdentityCookie,
Path(invite): Path<super::PathInfo>,
Json(request): Json<Request>,
-) -> Result<(IdentityCookie, Json<Login>), Error> {
+) -> Result<(IdentityCookie, Json<User>), Error> {
let (login, secret) = app
.invites()
.accept(&invite, &request.name, &request.password, &accepted_at)
diff --git a/src/invite/routes/invite/test/get.rs b/src/invite/routes/invite/test/get.rs
index c6780ed..0dc8a79 100644
--- a/src/invite/routes/invite/test/get.rs
+++ b/src/invite/routes/invite/test/get.rs
@@ -7,7 +7,7 @@ async fn valid_invite() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let issuer = fixtures::login::create(&app, &fixtures::now()).await;
+ let issuer = fixtures::user::create(&app, &fixtures::now()).await;
let invite = fixtures::invite::issue(&app, &issuer, &fixtures::now()).await;
// Call endpoint
@@ -45,7 +45,7 @@ async fn expired_invite() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let issuer = fixtures::login::create(&app, &fixtures::ancient()).await;
+ let issuer = fixtures::user::create(&app, &fixtures::ancient()).await;
let invite = fixtures::invite::issue(&app, &issuer, &fixtures::ancient()).await;
app.invites()
diff --git a/src/invite/routes/invite/test/post.rs b/src/invite/routes/invite/test/post.rs
index 40e0580..b204b32 100644
--- a/src/invite/routes/invite/test/post.rs
+++ b/src/invite/routes/invite/test/post.rs
@@ -11,12 +11,12 @@ async fn valid_invite() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let issuer = fixtures::login::create(&app, &fixtures::now()).await;
+ let issuer = fixtures::user::create(&app, &fixtures::now()).await;
let invite = fixtures::invite::issue(&app, &issuer, &fixtures::now()).await;
// Call the endpoint
- let (name, password) = fixtures::login::propose();
+ let (name, password) = fixtures::user::propose();
let identity = fixtures::cookie::not_logged_in();
let request = post::Request {
name: name.clone(),
@@ -68,7 +68,7 @@ async fn nonexistent_invite() {
// Call the endpoint
- let (name, password) = fixtures::login::propose();
+ let (name, password) = fixtures::user::propose();
let identity = fixtures::cookie::not_logged_in();
let request = post::Request {
name: name.clone(),
@@ -94,7 +94,7 @@ async fn expired_invite() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let issuer = fixtures::login::create(&app, &fixtures::ancient()).await;
+ let issuer = fixtures::user::create(&app, &fixtures::ancient()).await;
let invite = fixtures::invite::issue(&app, &issuer, &fixtures::ancient()).await;
app.invites()
@@ -104,7 +104,7 @@ async fn expired_invite() {
// Call the endpoint
- let (name, password) = fixtures::login::propose();
+ let (name, password) = fixtures::user::propose();
let identity = fixtures::cookie::not_logged_in();
let request = post::Request {
name: name.clone(),
@@ -130,10 +130,10 @@ async fn accepted_invite() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let issuer = fixtures::login::create(&app, &fixtures::ancient()).await;
+ let issuer = fixtures::user::create(&app, &fixtures::ancient()).await;
let invite = fixtures::invite::issue(&app, &issuer, &fixtures::ancient()).await;
- let (name, password) = fixtures::login::propose();
+ let (name, password) = fixtures::user::propose();
app.invites()
.accept(&invite.id, &name, &password, &fixtures::now())
.await
@@ -141,7 +141,7 @@ async fn accepted_invite() {
// Call the endpoint
- let (name, password) = fixtures::login::propose();
+ let (name, password) = fixtures::user::propose();
let identity = fixtures::cookie::not_logged_in();
let request = post::Request {
name: name.clone(),
@@ -167,23 +167,23 @@ async fn conflicting_name() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let issuer = fixtures::login::create(&app, &fixtures::ancient()).await;
+ let issuer = fixtures::user::create(&app, &fixtures::ancient()).await;
let invite = fixtures::invite::issue(&app, &issuer, &fixtures::ancient()).await;
let existing_name = Name::from("rijksmuseum");
- app.logins()
+ app.users()
.create(
&existing_name,
- &fixtures::login::propose_password(),
+ &fixtures::user::propose_password(),
&fixtures::now(),
)
.await
- .expect("creating a login in an empty environment succeeds");
+ .expect("creating a user in an empty environment succeeds");
// Call the endpoint
let conflicting_name = Name::from("r\u{0133}ksmuseum");
- let password = fixtures::login::propose_password();
+ let password = fixtures::user::propose_password();
let identity = fixtures::cookie::not_logged_in();
let request = post::Request {
@@ -212,13 +212,13 @@ async fn invalid_name() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let issuer = fixtures::login::create(&app, &fixtures::now()).await;
+ let issuer = fixtures::user::create(&app, &fixtures::now()).await;
let invite = fixtures::invite::issue(&app, &issuer, &fixtures::now()).await;
// Call the endpoint
- let name = fixtures::login::propose_invalid_name();
- let password = fixtures::login::propose_password();
+ let name = fixtures::user::propose_invalid_name();
+ let password = fixtures::user::propose_password();
let identity = fixtures::cookie::not_logged_in();
let request = post::Request {
name: name.clone(),
diff --git a/src/invite/routes/post.rs b/src/invite/routes/post.rs
index 898081e..f7ca76c 100644
--- a/src/invite/routes/post.rs
+++ b/src/invite/routes/post.rs
@@ -10,7 +10,7 @@ pub async fn handler(
identity: Identity,
_: Json<Request>,
) -> Result<Json<Invite>, Internal> {
- let invite = app.invites().issue(&identity.login, &issued_at).await?;
+ let invite = app.invites().issue(&identity.user, &issued_at).await?;
Ok(Json(invite))
}
diff --git a/src/invite/routes/test.rs b/src/invite/routes/test.rs
index 4d99660..4ea8a3d 100644
--- a/src/invite/routes/test.rs
+++ b/src/invite/routes/test.rs
@@ -23,6 +23,6 @@ async fn create_invite() {
.expect("creating an invite always succeeds");
// Verify the response
- assert_eq!(issuer.login.id, invite.issuer);
+ assert_eq!(issuer.user.id, invite.issuer);
assert_eq!(&*issued_at, &invite.issued_at);
}
diff --git a/src/lib.rs b/src/lib.rs
index 765e625..4cce63b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -14,7 +14,6 @@ mod event;
mod expire;
mod id;
mod invite;
-mod login;
mod message;
mod name;
mod normalize;
@@ -23,3 +22,4 @@ mod setup;
mod test;
mod token;
mod ui;
+mod user;
diff --git a/src/message/app.rs b/src/message/app.rs
index 6f8f3d4..3c74628 100644
--- a/src/message/app.rs
+++ b/src/message/app.rs
@@ -8,8 +8,8 @@ use crate::{
clock::DateTime,
db::NotFound as _,
event::{Broadcaster, Event, Sequence, repo::Provider as _},
- login::Login,
name,
+ user::User,
};
pub struct Messages<'a> {
@@ -25,7 +25,7 @@ impl<'a> Messages<'a> {
pub async fn send(
&self,
channel: &channel::Id,
- sender: &Login,
+ sender: &User,
sent_at: &DateTime,
body: &Body,
) -> Result<Message, SendError> {
@@ -47,7 +47,7 @@ impl<'a> Messages<'a> {
pub async fn delete(
&self,
- deleted_by: &Login,
+ deleted_by: &User,
message: &Id,
deleted_at: &DateTime,
) -> Result<(), DeleteError> {
@@ -146,8 +146,8 @@ impl From<channel::repo::LoadError> for SendError {
pub enum DeleteError {
#[error("message {0} not found")]
NotFound(Id),
- #[error("login {} not the message's sender", .0.id)]
- NotSender(Login),
+ #[error("user {} not the message's sender", .0.id)]
+ NotSender(User),
#[error("message {0} deleted")]
Deleted(Id),
#[error(transparent)]
diff --git a/src/message/repo.rs b/src/message/repo.rs
index 8a0a72c..9a4f72f 100644
--- a/src/message/repo.rs
+++ b/src/message/repo.rs
@@ -5,7 +5,7 @@ use crate::{
channel,
clock::DateTime,
event::{Instant, Sequence},
- login::{self, Login},
+ user::{self, User},
};
pub trait Provider {
@@ -24,7 +24,7 @@ impl Messages<'_> {
pub async fn create(
&mut self,
channel: &channel::History,
- sender: &Login,
+ sender: &User,
sent: &Instant,
body: &Body,
) -> Result<History, sqlx::Error> {
@@ -39,7 +39,7 @@ impl Messages<'_> {
returning
id as "id: Id",
channel as "channel: channel::Id",
- sender as "sender: login::Id",
+ sender as "sender: user::Id",
sent_at as "sent_at: DateTime",
sent_sequence as "sent_sequence: Sequence",
body as "body: Body"
@@ -75,7 +75,7 @@ impl Messages<'_> {
r#"
select
message.channel as "channel: channel::Id",
- message.sender as "sender: login::Id",
+ message.sender as "sender: user::Id",
id as "id: Id",
message.body as "body: Body",
message.sent_at as "sent_at: DateTime",
@@ -112,7 +112,7 @@ impl Messages<'_> {
r#"
select
message.channel as "channel: channel::Id",
- message.sender as "sender: login::Id",
+ message.sender as "sender: user::Id",
message.id as "id: Id",
message.body as "body: Body",
message.sent_at as "sent_at: DateTime",
@@ -149,7 +149,7 @@ impl Messages<'_> {
r#"
select
message.channel as "channel: channel::Id",
- message.sender as "sender: login::Id",
+ message.sender as "sender: user::Id",
id as "id: Id",
message.body as "body: Body",
message.sent_at as "sent_at: DateTime",
@@ -254,7 +254,7 @@ impl Messages<'_> {
select
id as "id: Id",
message.channel as "channel: channel::Id",
- message.sender as "sender: login::Id",
+ message.sender as "sender: user::Id",
message.sent_at as "sent_at: DateTime",
message.sent_sequence as "sent_sequence: Sequence",
message.body as "body: Body",
@@ -291,7 +291,7 @@ impl Messages<'_> {
select
id as "id: Id",
message.channel as "channel: channel::Id",
- message.sender as "sender: login::Id",
+ message.sender as "sender: user::Id",
message.sent_at as "sent_at: DateTime",
message.sent_sequence as "sent_sequence: Sequence",
message.body as "body: Body",
diff --git a/src/message/routes/message/mod.rs b/src/message/routes/message/mod.rs
index e92f556..4abd445 100644
--- a/src/message/routes/message/mod.rs
+++ b/src/message/routes/message/mod.rs
@@ -23,7 +23,7 @@ pub mod delete {
identity: Identity,
) -> Result<Response, Error> {
app.messages()
- .delete(&identity.login, &message, &deleted_at)
+ .delete(&identity.user, &message, &deleted_at)
.await?;
Ok(Response { id: message })
diff --git a/src/message/routes/message/test.rs b/src/message/routes/message/test.rs
index 5178ab5..1888be7 100644
--- a/src/message/routes/message/test.rs
+++ b/src/message/routes/message/test.rs
@@ -10,7 +10,7 @@ pub async fn delete_message() {
let app = fixtures::scratch_app().await;
let sender = fixtures::identity::create(&app, &fixtures::now()).await;
let channel = fixtures::channel::create(&app, &fixtures::now()).await;
- let message = fixtures::message::send(&app, &channel, &sender.login, &fixtures::now()).await;
+ let message = fixtures::message::send(&app, &channel, &sender.user, &fixtures::now()).await;
// Send the request
@@ -62,7 +62,7 @@ pub async fn delete_deleted() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let channel = fixtures::channel::create(&app, &fixtures::now()).await;
let message = fixtures::message::send(&app, &channel, &sender, &fixtures::now()).await;
@@ -93,7 +93,7 @@ pub async fn delete_expired() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let sender = fixtures::login::create(&app, &fixtures::ancient()).await;
+ let sender = fixtures::user::create(&app, &fixtures::ancient()).await;
let channel = fixtures::channel::create(&app, &fixtures::ancient()).await;
let message = fixtures::message::send(&app, &channel, &sender, &fixtures::ancient()).await;
@@ -124,7 +124,7 @@ pub async fn delete_purged() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let sender = fixtures::login::create(&app, &fixtures::ancient()).await;
+ let sender = fixtures::user::create(&app, &fixtures::ancient()).await;
let channel = fixtures::channel::create(&app, &fixtures::ancient()).await;
let message = fixtures::message::send(&app, &channel, &sender, &fixtures::ancient()).await;
@@ -160,7 +160,7 @@ pub async fn delete_not_sender() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let sender = fixtures::login::create(&app, &fixtures::now()).await;
+ let sender = fixtures::user::create(&app, &fixtures::now()).await;
let channel = fixtures::channel::create(&app, &fixtures::now()).await;
let message = fixtures::message::send(&app, &channel, &sender, &fixtures::now()).await;
@@ -179,6 +179,6 @@ pub async fn delete_not_sender() {
// Verify the response
assert!(
- matches!(error, app::DeleteError::NotSender(error_sender) if deleter.login == error_sender)
+ matches!(error, app::DeleteError::NotSender(error_sender) if deleter.user == error_sender)
);
}
diff --git a/src/message/snapshot.rs b/src/message/snapshot.rs
index d924ea1..ac067f7 100644
--- a/src/message/snapshot.rs
+++ b/src/message/snapshot.rs
@@ -2,14 +2,14 @@ use super::{
Body, Id,
event::{Event, Sent},
};
-use crate::{channel, clock::DateTime, event::Instant, login};
+use crate::{channel, clock::DateTime, event::Instant, user};
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
pub struct Message {
#[serde(flatten)]
pub sent: Instant,
pub channel: channel::Id,
- pub sender: login::Id,
+ pub sender: user::Id,
pub id: Id,
pub body: Body,
#[serde(skip_serializing_if = "Option::is_none")]
diff --git a/src/setup/app.rs b/src/setup/app.rs
index 9553f40..26eed7a 100644
--- a/src/setup/app.rs
+++ b/src/setup/app.rs
@@ -4,12 +4,12 @@ use super::repo::Provider as _;
use crate::{
clock::DateTime,
event::Broadcaster,
- login::{
- Login, Password,
- create::{self, Create},
- },
name::Name,
token::{Secret, repo::Provider as _},
+ user::{
+ Password, User,
+ create::{self, Create},
+ },
};
pub struct Setup<'a> {
@@ -27,7 +27,7 @@ impl<'a> Setup<'a> {
name: &Name,
password: &Password,
created_at: &DateTime,
- ) -> Result<(Login, Secret), Error> {
+ ) -> Result<(User, Secret), Error> {
let create = Create::begin(name, password, created_at);
let validated = create.validate()?;
@@ -38,12 +38,12 @@ impl<'a> Setup<'a> {
} else {
validated.store(&mut tx).await?
};
- let secret = tx.tokens().issue(stored.login(), created_at).await?;
+ let secret = tx.tokens().issue(stored.user(), created_at).await?;
tx.commit().await?;
- let login = stored.publish(self.events);
+ let user = stored.publish(self.events);
- Ok((login.as_created(), secret))
+ Ok((user.as_created(), secret))
}
pub async fn completed(&self) -> Result<bool, sqlx::Error> {
@@ -59,7 +59,7 @@ impl<'a> Setup<'a> {
pub enum Error {
#[error("initial setup previously completed")]
SetupCompleted,
- #[error("invalid login name: {0}")]
+ #[error("invalid user name: {0}")]
InvalidName(Name),
#[error(transparent)]
Database(#[from] sqlx::Error),
diff --git a/src/setup/repo.rs b/src/setup/repo.rs
index ac01496..c4f5fd8 100644
--- a/src/setup/repo.rs
+++ b/src/setup/repo.rs
@@ -17,7 +17,7 @@ impl Setup<'_> {
let completed = sqlx::query_scalar!(
r#"
select count(*) > 0 as "completed: bool"
- from login
+ from user
"#,
)
.fetch_one(&mut *self.0)
diff --git a/src/setup/routes/post.rs b/src/setup/routes/post.rs
index 2a46b04..0ff5d69 100644
--- a/src/setup/routes/post.rs
+++ b/src/setup/routes/post.rs
@@ -8,10 +8,10 @@ use crate::{
app::App,
clock::RequestedAt,
error::Internal,
- login::{Login, Password},
name::Name,
setup::app,
token::extract::IdentityCookie,
+ user::{Password, User},
};
pub async fn handler(
@@ -19,14 +19,14 @@ pub async fn handler(
RequestedAt(setup_at): RequestedAt,
identity: IdentityCookie,
Json(request): Json<Request>,
-) -> Result<(IdentityCookie, Json<Login>), Error> {
- let (login, secret) = app
+) -> Result<(IdentityCookie, Json<User>), Error> {
+ let (user, secret) = app
.setup()
.initial(&request.name, &request.password, &setup_at)
.await
.map_err(Error)?;
let identity = identity.set(secret);
- Ok((identity, Json(login)))
+ Ok((identity, Json(user)))
}
#[derive(serde::Deserialize)]
diff --git a/src/setup/routes/test.rs b/src/setup/routes/test.rs
index 5794b78..e9f5cd6 100644
--- a/src/setup/routes/test.rs
+++ b/src/setup/routes/test.rs
@@ -11,7 +11,7 @@ async fn fresh_instance() {
// Call the endpoint
let identity = fixtures::cookie::not_logged_in();
- let (name, password) = fixtures::login::propose();
+ let (name, password) = fixtures::user::propose();
let request = post::Request {
name: name.clone(),
password: password.clone(),
@@ -52,11 +52,11 @@ async fn login_exists() {
// Set up the environment
let app = fixtures::scratch_app().await;
- fixtures::login::create(&app, &fixtures::now()).await;
+ fixtures::user::create(&app, &fixtures::now()).await;
// Call the endpoint
let identity = fixtures::cookie::not_logged_in();
- let (name, password) = fixtures::login::propose();
+ let (name, password) = fixtures::user::propose();
let request = post::Request { name, password };
let post::Error(error) =
post::handler(State(app.clone()), fixtures::now(), identity, Json(request))
@@ -76,8 +76,8 @@ async fn invalid_name() {
// Call the endpoint
- let name = fixtures::login::propose_invalid_name();
- let password = fixtures::login::propose_password();
+ let name = fixtures::user::propose_invalid_name();
+ let password = fixtures::user::propose_password();
let identity = fixtures::cookie::not_logged_in();
let request = post::Request {
name: name.clone(),
diff --git a/src/test/fixtures/cookie.rs b/src/test/fixtures/cookie.rs
index fcb379f..bba53b8 100644
--- a/src/test/fixtures/cookie.rs
+++ b/src/test/fixtures/cookie.rs
@@ -3,9 +3,9 @@ use uuid::Uuid;
use crate::{
app::App,
clock::RequestedAt,
- login::Password,
name::Name,
token::{Secret, extract::IdentityCookie},
+ user::Password,
};
pub fn not_logged_in() -> IdentityCookie {
diff --git a/src/test/fixtures/event.rs b/src/test/fixtures/event.rs
index e11f6ee..a30bb4b 100644
--- a/src/test/fixtures/event.rs
+++ b/src/test/fixtures/event.rs
@@ -16,9 +16,9 @@ pub fn message(event: Event) -> Ready<Option<message::Event>> {
})
}
-pub fn login(event: Event) -> Ready<Option<login::Event>> {
+pub fn user(event: Event) -> Ready<Option<user::Event>> {
future::ready(match event {
- Event::Login(event) => Some(event),
+ Event::User(event) => Some(event),
_ => None,
})
}
@@ -65,11 +65,11 @@ pub mod message {
}
}
-pub mod login {
+pub mod user {
use std::future::{self, Ready};
- pub use crate::login::Event;
- use crate::login::event;
+ pub use crate::user::Event;
+ use crate::user::event;
pub fn created(event: Event) -> Ready<Option<event::Created>> {
future::ready(match event {
diff --git a/src/test/fixtures/identity.rs b/src/test/fixtures/identity.rs
index ffc44c6..cb325d8 100644
--- a/src/test/fixtures/identity.rs
+++ b/src/test/fixtures/identity.rs
@@ -1,17 +1,17 @@
use crate::{
app::App,
clock::RequestedAt,
- login::Password,
name::Name,
test::fixtures,
token::{
self,
extract::{Identity, IdentityCookie},
},
+ user::Password,
};
pub async fn create(app: &App, created_at: &RequestedAt) -> Identity {
- let credentials = fixtures::login::create_with_password(app, created_at).await;
+ let credentials = fixtures::user::create_with_password(app, created_at).await;
logged_in(app, &credentials, created_at).await
}
@@ -21,13 +21,13 @@ pub async fn from_cookie(
validated_at: &RequestedAt,
) -> Identity {
let secret = cookie.secret().expect("identity token has a secret");
- let (token, login) = app
+ let (token, user) = app
.tokens()
.validate(&secret, validated_at)
.await
.expect("always validates newly-issued secret");
- Identity { token, login }
+ Identity { token, user }
}
pub async fn logged_in(
@@ -41,7 +41,7 @@ pub async fn logged_in(
pub fn fictitious() -> Identity {
let token = token::Id::generate();
- let login = fixtures::login::fictitious();
+ let user = fixtures::user::fictitious();
- Identity { token, login }
+ Identity { token, user }
}
diff --git a/src/test/fixtures/invite.rs b/src/test/fixtures/invite.rs
index 654d1b4..7a41eb6 100644
--- a/src/test/fixtures/invite.rs
+++ b/src/test/fixtures/invite.rs
@@ -2,10 +2,10 @@ use crate::{
app::App,
clock::DateTime,
invite::{self, Invite},
- login::Login,
+ user::User,
};
-pub async fn issue(app: &App, issuer: &Login, issued_at: &DateTime) -> Invite {
+pub async fn issue(app: &App, issuer: &User, issued_at: &DateTime) -> Invite {
app.invites()
.issue(issuer, issued_at)
.await
diff --git a/src/test/fixtures/message.rs b/src/test/fixtures/message.rs
index d3b4719..2254915 100644
--- a/src/test/fixtures/message.rs
+++ b/src/test/fixtures/message.rs
@@ -4,11 +4,11 @@ use crate::{
app::App,
channel::Channel,
clock::RequestedAt,
- login::Login,
message::{self, Body, Message},
+ user::User,
};
-pub async fn send(app: &App, channel: &Channel, sender: &Login, sent_at: &RequestedAt) -> Message {
+pub async fn send(app: &App, channel: &Channel, sender: &User, sent_at: &RequestedAt) -> Message {
let body = propose();
app.messages()
diff --git a/src/test/fixtures/mod.rs b/src/test/fixtures/mod.rs
index 57eee30..418bdb5 100644
--- a/src/test/fixtures/mod.rs
+++ b/src/test/fixtures/mod.rs
@@ -9,8 +9,8 @@ pub mod event;
pub mod future;
pub mod identity;
pub mod invite;
-pub mod login;
pub mod message;
+pub mod user;
pub async fn scratch_app() -> App {
let pool = db::prepare("sqlite::memory:", "sqlite::memory:")
diff --git a/src/test/fixtures/login.rs b/src/test/fixtures/user.rs
index 86e3e39..6448f64 100644
--- a/src/test/fixtures/login.rs
+++ b/src/test/fixtures/user.rs
@@ -4,32 +4,32 @@ use uuid::Uuid;
use crate::{
app::App,
clock::RequestedAt,
- login::{self, Login, Password},
name::Name,
+ user::{self, Password, User},
};
pub async fn create_with_password(app: &App, created_at: &RequestedAt) -> (Name, Password) {
let (name, password) = propose();
- let login = app
- .logins()
+ let user = app
+ .users()
.create(&name, &password, created_at)
.await
.expect("should always succeed if the login is actually new");
- (login.name, password)
+ (user.name, password)
}
-pub async fn create(app: &App, created_at: &RequestedAt) -> Login {
+pub async fn create(app: &App, created_at: &RequestedAt) -> User {
let (name, password) = propose();
- app.logins()
+ app.users()
.create(&name, &password, created_at)
.await
.expect("should always succeed if the login is actually new")
}
-pub fn fictitious() -> Login {
- Login {
- id: login::Id::generate(),
+pub fn fictitious() -> User {
+ User {
+ id: user::Id::generate(),
name: propose_name(),
}
}
diff --git a/src/token/app.rs b/src/token/app.rs
index 3f054ff..46b2d73 100644
--- a/src/token/app.rs
+++ b/src/token/app.rs
@@ -12,8 +12,8 @@ use super::{
use crate::{
clock::DateTime,
db::NotFound as _,
- login::{Login, Password, repo::Provider as _},
name::{self, Name},
+ user::{Password, User, repo::Provider as _},
};
pub struct Tokens<'a> {
@@ -31,9 +31,9 @@ impl<'a> Tokens<'a> {
name: &Name,
password: &Password,
login_at: &DateTime,
- ) -> Result<(Login, Secret), LoginError> {
+ ) -> Result<(User, Secret), LoginError> {
let mut tx = self.db.begin().await?;
- let (login, stored_hash) = tx
+ let (user, stored_hash) = tx
.auth()
.for_name(name)
.await
@@ -46,11 +46,11 @@ impl<'a> Tokens<'a> {
// if the account is deleted during that time.
tx.commit().await?;
- let snapshot = login.as_snapshot().ok_or(LoginError::Rejected)?;
+ let snapshot = user.as_snapshot().ok_or(LoginError::Rejected)?;
let token = if stored_hash.verify(password)? {
let mut tx = self.db.begin().await?;
- let token = tx.tokens().issue(&login, login_at).await?;
+ let token = tx.tokens().issue(&user, login_at).await?;
tx.commit().await?;
token
} else {
@@ -62,15 +62,15 @@ impl<'a> Tokens<'a> {
pub async fn change_password(
&self,
- login: &Login,
+ user: &User,
password: &Password,
to: &Password,
changed_at: &DateTime,
- ) -> Result<(Login, Secret), LoginError> {
+ ) -> Result<(User, Secret), LoginError> {
let mut tx = self.db.begin().await?;
- let (login, stored_hash) = tx
+ let (user, stored_hash) = tx
.auth()
- .for_login(login)
+ .for_user(user)
.await
.optional()?
.ok_or(LoginError::Rejected)?;
@@ -85,13 +85,13 @@ impl<'a> Tokens<'a> {
return Err(LoginError::Rejected);
}
- let snapshot = login.as_snapshot().ok_or(LoginError::Rejected)?;
+ let snapshot = user.as_snapshot().ok_or(LoginError::Rejected)?;
let to_hash = to.hash()?;
let mut tx = self.db.begin().await?;
- let tokens = tx.tokens().revoke_all(&login).await?;
- tx.logins().set_password(&login, &to_hash).await?;
- let secret = tx.tokens().issue(&login, changed_at).await?;
+ let tokens = tx.tokens().revoke_all(&user).await?;
+ tx.users().set_password(&user, &to_hash).await?;
+ let secret = tx.tokens().issue(&user, changed_at).await?;
tx.commit().await?;
for event in tokens.into_iter().map(TokenEvent::Revoked) {
@@ -105,18 +105,18 @@ impl<'a> Tokens<'a> {
&self,
secret: &Secret,
used_at: &DateTime,
- ) -> Result<(Id, Login), ValidateError> {
+ ) -> Result<(Id, User), ValidateError> {
let mut tx = self.db.begin().await?;
- let (token, login) = tx
+ let (token, user) = tx
.tokens()
.validate(secret, used_at)
.await
.not_found(|| ValidateError::InvalidToken)?;
tx.commit().await?;
- let login = login.as_snapshot().ok_or(ValidateError::LoginDeleted)?;
+ let user = user.as_snapshot().ok_or(ValidateError::LoginDeleted)?;
- Ok((token, login))
+ Ok((token, user))
}
pub async fn limit_stream<S, E>(
@@ -226,7 +226,7 @@ impl From<repo::auth::LoadError> for LoginError {
pub enum ValidateError {
#[error("invalid token")]
InvalidToken,
- #[error("login deleted")]
+ #[error("user deleted")]
LoginDeleted,
#[error(transparent)]
Database(#[from] sqlx::Error),
diff --git a/src/token/extract/identity.rs b/src/token/extract/identity.rs
index acfd7ae..d1c0334 100644
--- a/src/token/extract/identity.rs
+++ b/src/token/extract/identity.rs
@@ -10,14 +10,14 @@ use crate::{
app::App,
clock::RequestedAt,
error::{Internal, Unauthorized},
- login::Login,
token::{self, app::ValidateError},
+ user::User,
};
#[derive(Clone, Debug)]
pub struct Identity {
pub token: token::Id,
- pub login: Login,
+ pub user: User,
}
impl FromRequestParts<App> for Identity {
@@ -31,7 +31,7 @@ impl FromRequestParts<App> for Identity {
let app = State::<App>::from_request_parts(parts, state).await?;
match app.tokens().validate(&secret, &used_at).await {
- Ok((token, login)) => Ok(Identity { token, login }),
+ Ok((token, user)) => Ok(Identity { token, user }),
Err(ValidateError::InvalidToken) => Err(LoginError::Unauthorized),
Err(other) => Err(other.into()),
}
diff --git a/src/token/repo/auth.rs b/src/token/repo/auth.rs
index 0deed10..68a81c7 100644
--- a/src/token/repo/auth.rs
+++ b/src/token/repo/auth.rs
@@ -4,8 +4,8 @@ use crate::{
clock::DateTime,
db::NotFound,
event::{Instant, Sequence},
- login::{self, History, Login, password::StoredHash},
name::{self, Name},
+ user::{self, History, User, password::StoredHash},
};
pub trait Provider {
@@ -26,13 +26,13 @@ impl Auth<'_> {
let row = sqlx::query!(
r#"
select
- id as "id: login::Id",
+ id as "id: user::Id",
display_name as "display_name: String",
canonical_name as "canonical_name: String",
created_sequence as "created_sequence: Sequence",
created_at as "created_at: DateTime",
password_hash as "password_hash: StoredHash"
- from login
+ from user
where canonical_name = $1
"#,
name,
@@ -41,7 +41,7 @@ impl Auth<'_> {
.await?;
let login = History {
- login: Login {
+ user: User {
id: row.id,
name: Name::new(row.display_name, row.canonical_name)?,
},
@@ -51,33 +51,33 @@ impl Auth<'_> {
Ok((login, row.password_hash))
}
- pub async fn for_login(&mut self, login: &Login) -> Result<(History, StoredHash), LoadError> {
+ pub async fn for_user(&mut self, user: &User) -> Result<(History, StoredHash), LoadError> {
let row = sqlx::query!(
r#"
select
- id as "id: login::Id",
+ id as "id: user::Id",
display_name as "display_name: String",
canonical_name as "canonical_name: String",
created_sequence as "created_sequence: Sequence",
created_at as "created_at: DateTime",
password_hash as "password_hash: StoredHash"
- from login
+ from user
where id = $1
"#,
- login.id,
+ user.id,
)
.fetch_one(&mut *self.0)
.await?;
- let login = History {
- login: Login {
+ let user = History {
+ user: User {
id: row.id,
name: Name::new(row.display_name, row.canonical_name)?,
},
created: Instant::new(row.created_at, row.created_sequence),
};
- Ok((login, row.password_hash))
+ Ok((user, row.password_hash))
}
}
diff --git a/src/token/repo/token.rs b/src/token/repo/token.rs
index ff42fad..e49c2d4 100644
--- a/src/token/repo/token.rs
+++ b/src/token/repo/token.rs
@@ -5,9 +5,9 @@ use crate::{
clock::DateTime,
db::NotFound,
event::{Instant, Sequence},
- login::{self, History, Login},
name::{self, Name},
token::{Id, Secret},
+ user::{self, History, User},
};
pub trait Provider {
@@ -27,23 +27,23 @@ impl Tokens<'_> {
// be used to control expiry, until the token is actually used.
pub async fn issue(
&mut self,
- login: &History,
+ user: &History,
issued_at: &DateTime,
) -> Result<Secret, sqlx::Error> {
let id = Id::generate();
let secret = Uuid::new_v4().to_string();
- let login = login.id();
+ let user = user.id();
let secret = sqlx::query_scalar!(
r#"
insert
- into token (id, secret, login, issued_at, last_used_at)
+ into token (id, secret, user, issued_at, last_used_at)
values ($1, $2, $3, $4, $4)
returning secret as "secret!: Secret"
"#,
id,
secret,
- login,
+ user,
issued_at,
)
.fetch_one(&mut *self.0)
@@ -85,16 +85,16 @@ impl Tokens<'_> {
}
// Revoke tokens for a login
- pub async fn revoke_all(&mut self, login: &login::History) -> Result<Vec<Id>, sqlx::Error> {
- let login = login.id();
+ pub async fn revoke_all(&mut self, user: &user::History) -> Result<Vec<Id>, sqlx::Error> {
+ let user = user.id();
let tokens = sqlx::query_scalar!(
r#"
delete
from token
- where login = $1
+ where user = $1
returning id as "id: Id"
"#,
- login,
+ user,
)
.fetch_all(&mut *self.0)
.await?;
@@ -132,38 +132,38 @@ impl Tokens<'_> {
// sqlite3, as of this writing, does not allow an update's `returning`
// clause to reference columns from tables joined into the update. Two
// queries is fine, but it feels untidy.
- let (token, login) = sqlx::query!(
+ let (token, user) = sqlx::query!(
r#"
update token
set last_used_at = $1
where secret = $2
returning
id as "token: Id",
- login as "login: login::Id"
+ user as "user: user::Id"
"#,
used_at,
secret,
)
- .map(|row| (row.token, row.login))
+ .map(|row| (row.token, row.user))
.fetch_one(&mut *self.0)
.await?;
- let login = sqlx::query!(
+ let user = sqlx::query!(
r#"
select
- id as "id: login::Id",
+ id as "id: user::Id",
display_name as "display_name: String",
canonical_name as "canonical_name: String",
created_sequence as "created_sequence: Sequence",
created_at as "created_at: DateTime"
- from login
+ from user
where id = $1
"#,
- login,
+ user,
)
.map(|row| {
Ok::<_, name::Error>(History {
- login: Login {
+ user: User {
id: row.id,
name: Name::new(row.display_name, row.canonical_name)?,
},
@@ -173,7 +173,7 @@ impl Tokens<'_> {
.fetch_one(&mut *self.0)
.await??;
- Ok((token, login))
+ Ok((token, user))
}
}
diff --git a/src/login/app.rs b/src/user/app.rs
index 2da4d6a..2ab356f 100644
--- a/src/login/app.rs
+++ b/src/user/app.rs
@@ -1,17 +1,17 @@
use sqlx::sqlite::SqlitePool;
use super::{
- Login, Password,
+ Password, User,
create::{self, Create},
};
use crate::{clock::DateTime, event::Broadcaster, name::Name};
-pub struct Logins<'a> {
+pub struct Users<'a> {
db: &'a SqlitePool,
events: &'a Broadcaster,
}
-impl<'a> Logins<'a> {
+impl<'a> Users<'a> {
pub const fn new(db: &'a SqlitePool, events: &'a Broadcaster) -> Self {
Self { db, events }
}
@@ -21,7 +21,7 @@ impl<'a> Logins<'a> {
name: &Name,
password: &Password,
created_at: &DateTime,
- ) -> Result<Login, CreateError> {
+ ) -> Result<User, CreateError> {
let create = Create::begin(name, password, created_at);
let validated = create.validate()?;
@@ -29,15 +29,15 @@ impl<'a> Logins<'a> {
let stored = validated.store(&mut tx).await?;
tx.commit().await?;
- let login = stored.publish(self.events);
+ let user = stored.publish(self.events);
- Ok(login.as_created())
+ Ok(user.as_created())
}
}
#[derive(Debug, thiserror::Error)]
pub enum CreateError {
- #[error("invalid login name: {0}")]
+ #[error("invalid user name: {0}")]
InvalidName(Name),
#[error(transparent)]
PasswordHash(#[from] password_hash::Error),
diff --git a/src/login/create.rs b/src/user/create.rs
index c5cea08..da94685 100644
--- a/src/login/create.rs
+++ b/src/user/create.rs
@@ -1,13 +1,13 @@
-use sqlx::{sqlite::Sqlite, Transaction};
+use sqlx::{Transaction, sqlite::Sqlite};
-use super::{password::StoredHash, repo::Provider as _, validate, History, Password};
+use super::{History, Password, password::StoredHash, repo::Provider as _, validate};
use crate::{
clock::DateTime,
- event::{repo::Provider as _, Broadcaster, Event},
+ event::{Broadcaster, Event, repo::Provider as _},
name::Name,
};
-#[must_use = "dropping a login creation attempt is likely a mistake"]
+#[must_use = "dropping a user creation attempt is likely a mistake"]
pub struct Create<'a> {
name: &'a Name,
password: &'a Password,
@@ -44,7 +44,7 @@ impl<'a> Create<'a> {
}
}
-#[must_use = "dropping a login creation attempt is likely a mistake"]
+#[must_use = "dropping a user creation attempt is likely a mistake"]
pub struct Validated<'a> {
name: &'a Name,
password_hash: StoredHash,
@@ -60,35 +60,35 @@ impl Validated<'_> {
} = self;
let created = tx.sequence().next(created_at).await?;
- let login = tx.logins().create(name, &password_hash, &created).await?;
+ let user = tx.users().create(name, &password_hash, &created).await?;
- Ok(Stored { login })
+ Ok(Stored { user })
}
}
-#[must_use = "dropping a login creation attempt is likely a mistake"]
+#[must_use = "dropping a user creation attempt is likely a mistake"]
pub struct Stored {
- login: History,
+ user: History,
}
impl Stored {
- #[must_use = "dropping a login creation attempt is likely a mistake"]
+ #[must_use = "dropping a user creation attempt is likely a mistake"]
pub fn publish(self, events: &Broadcaster) -> History {
- let Self { login } = self;
+ let Self { user } = self;
- events.broadcast(login.events().map(Event::from).collect::<Vec<_>>());
+ events.broadcast(user.events().map(Event::from).collect::<Vec<_>>());
- login
+ user
}
- pub fn login(&self) -> &History {
- &self.login
+ pub fn user(&self) -> &History {
+ &self.user
}
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
- #[error("invalid login name: {0}")]
+ #[error("invalid user name: {0}")]
InvalidName(Name),
#[error(transparent)]
PasswordHash(#[from] password_hash::Error),
diff --git a/src/login/event.rs b/src/user/event.rs
index b03451a..a748112 100644
--- a/src/login/event.rs
+++ b/src/user/event.rs
@@ -1,4 +1,4 @@
-use super::snapshot::Login;
+use super::snapshot::User;
use crate::event::{Instant, Sequenced};
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
@@ -20,7 +20,7 @@ pub struct Created {
#[serde(flatten)]
pub instant: Instant,
#[serde(flatten)]
- pub login: Login,
+ pub user: User,
}
impl Sequenced for Created {
diff --git a/src/login/history.rs b/src/user/history.rs
index d67bcce..ae7a561 100644
--- a/src/login/history.rs
+++ b/src/user/history.rs
@@ -1,37 +1,37 @@
use super::{
- Id, Login,
+ Id, User,
event::{Created, Event},
};
use crate::event::{Instant, Sequence};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct History {
- pub login: Login,
+ pub user: User,
pub created: Instant,
}
// State interface
impl History {
pub fn id(&self) -> &Id {
- &self.login.id
+ &self.user.id
}
- // Snapshot of this login as it was when created. (Note to the future: it's okay
- // if this returns a redacted or modified version of the login. If we implement
+ // Snapshot of this user as it was when created. (Note to the future: it's okay
+ // if this returns a redacted or modified version of the user. If we implement
// renames by redacting the original name, then this should return the edited
- // login, not the original, even if that's not how it was "as created.")
- pub fn as_created(&self) -> Login {
- self.login.clone()
+ // user, not the original, even if that's not how it was "as created.")
+ pub fn as_created(&self) -> User {
+ self.user.clone()
}
- pub fn as_of(&self, resume_point: Sequence) -> Option<Login> {
+ pub fn as_of(&self, resume_point: Sequence) -> Option<User> {
self.events()
.filter(Sequence::up_to(resume_point))
.collect()
}
- // Snapshot of this login, as of all events recorded in this history.
- pub fn as_snapshot(&self) -> Option<Login> {
+ // Snapshot of this user, as of all events recorded in this history.
+ pub fn as_snapshot(&self) -> Option<User> {
self.events().collect()
}
}
@@ -41,7 +41,7 @@ impl History {
fn created(&self) -> Event {
Created {
instant: self.created,
- login: self.login.clone(),
+ user: self.user.clone(),
}
.into()
}
diff --git a/src/login/id.rs b/src/user/id.rs
index c46d697..bc14c1f 100644
--- a/src/login/id.rs
+++ b/src/user/id.rs
@@ -1,6 +1,7 @@
use crate::id::Id as BaseId;
-// Stable identifier for a [Login]. Prefixed with `L`.
+// Stable identifier for a User. Prefixed with `U`. Users created before March, 2025 may have an `L`
+// prefix, instead.
#[derive(Clone, Debug, Eq, PartialEq, sqlx::Type, serde::Serialize)]
#[sqlx(transparent)]
pub struct Id(BaseId);
@@ -13,7 +14,7 @@ impl From<BaseId> for Id {
impl Id {
pub fn generate() -> Self {
- BaseId::generate("L")
+ BaseId::generate("U")
}
}
diff --git a/src/login/mod.rs b/src/user/mod.rs
index 006fa0c..f4c66ab 100644
--- a/src/login/mod.rs
+++ b/src/user/mod.rs
@@ -11,5 +11,5 @@ mod snapshot;
mod validate;
pub use self::{
- event::Event, history::History, id::Id, password::Password, routes::router, snapshot::Login,
+ event::Event, history::History, id::Id, password::Password, routes::router, snapshot::User,
};
diff --git a/src/login/password.rs b/src/user/password.rs
index e1d164e..e1d164e 100644
--- a/src/login/password.rs
+++ b/src/user/password.rs
diff --git a/src/login/repo.rs b/src/user/repo.rs
index 03f2c17..c02d50f 100644
--- a/src/login/repo.rs
+++ b/src/user/repo.rs
@@ -4,23 +4,23 @@ use sqlx::{SqliteConnection, Transaction, sqlite::Sqlite};
use crate::{
clock::DateTime,
event::{Instant, Sequence},
- login::{History, Id, Login, password::StoredHash},
name::{self, Name},
+ user::{History, Id, User, password::StoredHash},
};
pub trait Provider {
- fn logins(&mut self) -> Logins;
+ fn users(&mut self) -> Users;
}
impl Provider for Transaction<'_, Sqlite> {
- fn logins(&mut self) -> Logins {
- Logins(self)
+ fn users(&mut self) -> Users {
+ Users(self)
}
}
-pub struct Logins<'t>(&'t mut SqliteConnection);
+pub struct Users<'t>(&'t mut SqliteConnection);
-impl Logins<'_> {
+impl Users<'_> {
pub async fn create(
&mut self,
name: &Name,
@@ -34,7 +34,7 @@ impl Logins<'_> {
sqlx::query!(
r#"
insert
- into login (id, display_name, canonical_name, password_hash, created_sequence, created_at)
+ into user (id, display_name, canonical_name, password_hash, created_sequence, created_at)
values ($1, $2, $3, $4, $5, $6)
"#,
id,
@@ -47,15 +47,15 @@ impl Logins<'_> {
.execute(&mut *self.0)
.await?;
- let login = History {
+ let user = History {
created: *created,
- login: Login {
+ user: User {
id,
name: name.clone(),
},
};
- Ok(login)
+ Ok(user)
}
pub async fn set_password(
@@ -67,7 +67,7 @@ impl Logins<'_> {
sqlx::query_scalar!(
r#"
- update login
+ update user
set password_hash = $1
where id = $2
returning id as "id: Id"
@@ -90,7 +90,7 @@ impl Logins<'_> {
canonical_name as "canonical_name: String",
created_sequence as "created_sequence: Sequence",
created_at as "created_at: DateTime"
- from login
+ from user
where created_sequence <= $1
order by canonical_name
"#,
@@ -98,7 +98,7 @@ impl Logins<'_> {
)
.map(|row| {
Ok::<_, LoadError>(History {
- login: Login {
+ user: User {
id: row.id,
name: Name::new(row.display_name, row.canonical_name)?,
},
@@ -122,14 +122,14 @@ impl Logins<'_> {
canonical_name as "canonical_name: String",
created_sequence as "created_sequence: Sequence",
created_at as "created_at: DateTime"
- from login
- where login.created_sequence > $1
+ from user
+ where created_sequence > $1
"#,
resume_at,
)
.map(|row| {
Ok::<_, name::Error>(History {
- login: Login {
+ user: User {
id: row.id,
name: Name::new(row.display_name, row.canonical_name)?,
},
diff --git a/src/login/routes/login/mod.rs b/src/user/routes/login/mod.rs
index 36b384e..36b384e 100644
--- a/src/login/routes/login/mod.rs
+++ b/src/user/routes/login/mod.rs
diff --git a/src/login/routes/login/post.rs b/src/user/routes/login/post.rs
index 96da5c5..39f9eea 100644
--- a/src/login/routes/login/post.rs
+++ b/src/user/routes/login/post.rs
@@ -8,9 +8,9 @@ use crate::{
app::App,
clock::RequestedAt,
error::Internal,
- login::{Login, Password},
name::Name,
token::{app, extract::IdentityCookie},
+ user::{Password, User},
};
pub async fn handler(
@@ -18,14 +18,14 @@ pub async fn handler(
RequestedAt(now): RequestedAt,
identity: IdentityCookie,
Json(request): Json<Request>,
-) -> Result<(IdentityCookie, Json<Login>), Error> {
- let (login, secret) = app
+) -> Result<(IdentityCookie, Json<User>), Error> {
+ let (user, secret) = app
.tokens()
.login(&request.name, &request.password, &now)
.await
.map_err(Error)?;
let identity = identity.set(secret);
- Ok((identity, Json(login)))
+ Ok((identity, Json(user)))
}
#[derive(serde::Deserialize)]
diff --git a/src/login/routes/login/test.rs b/src/user/routes/login/test.rs
index 7399796..d2e7ee2 100644
--- a/src/login/routes/login/test.rs
+++ b/src/user/routes/login/test.rs
@@ -8,7 +8,7 @@ async fn correct_credentials() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let (name, password) = fixtures::login::create_with_password(&app, &fixtures::now()).await;
+ let (name, password) = fixtures::user::create_with_password(&app, &fixtures::now()).await;
// Call the endpoint
@@ -52,7 +52,7 @@ async fn invalid_name() {
let identity = fixtures::cookie::not_logged_in();
let logged_in_at = fixtures::now();
- let (name, password) = fixtures::login::propose();
+ let (name, password) = fixtures::user::propose();
let request = post::Request {
name: name.clone(),
password,
@@ -72,7 +72,7 @@ async fn incorrect_password() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let login = fixtures::login::create(&app, &fixtures::now()).await;
+ let login = fixtures::user::create(&app, &fixtures::now()).await;
// Call the endpoint
@@ -80,7 +80,7 @@ async fn incorrect_password() {
let identity = fixtures::cookie::not_logged_in();
let request = post::Request {
name: login.name,
- password: fixtures::login::propose_password(),
+ password: fixtures::user::propose_password(),
};
let post::Error(error) =
post::handler(State(app.clone()), logged_in_at, identity, Json(request))
@@ -97,7 +97,7 @@ async fn token_expires() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let (name, password) = fixtures::login::create_with_password(&app, &fixtures::now()).await;
+ let (name, password) = fixtures::user::create_with_password(&app, &fixtures::now()).await;
// Call the endpoint
diff --git a/src/login/routes/logout/mod.rs b/src/user/routes/logout/mod.rs
index 36b384e..36b384e 100644
--- a/src/login/routes/logout/mod.rs
+++ b/src/user/routes/logout/mod.rs
diff --git a/src/login/routes/logout/post.rs b/src/user/routes/logout/post.rs
index bb09b9f..bb09b9f 100644
--- a/src/login/routes/logout/post.rs
+++ b/src/user/routes/logout/post.rs
diff --git a/src/login/routes/logout/test.rs b/src/user/routes/logout/test.rs
index 775fa9f..ce93760 100644
--- a/src/login/routes/logout/test.rs
+++ b/src/user/routes/logout/test.rs
@@ -12,7 +12,7 @@ async fn successful() {
let app = fixtures::scratch_app().await;
let now = fixtures::now();
- let creds = fixtures::login::create_with_password(&app, &fixtures::now()).await;
+ let creds = fixtures::user::create_with_password(&app, &fixtures::now()).await;
let identity = fixtures::cookie::logged_in(&app, &creds, &now).await;
let secret = fixtures::cookie::secret(&identity);
diff --git a/src/login/routes/mod.rs b/src/user/routes/mod.rs
index ade96cb..ade96cb 100644
--- a/src/login/routes/mod.rs
+++ b/src/user/routes/mod.rs
diff --git a/src/login/routes/password/mod.rs b/src/user/routes/password/mod.rs
index 36b384e..36b384e 100644
--- a/src/login/routes/password/mod.rs
+++ b/src/user/routes/password/mod.rs
diff --git a/src/login/routes/password/post.rs b/src/user/routes/password/post.rs
index 4723754..296f6cd 100644
--- a/src/login/routes/password/post.rs
+++ b/src/user/routes/password/post.rs
@@ -8,11 +8,11 @@ use crate::{
app::App,
clock::RequestedAt,
error::Internal,
- login::{Login, Password},
token::{
app,
extract::{Identity, IdentityCookie},
},
+ user::{Password, User},
};
pub async fn handler(
@@ -21,10 +21,10 @@ pub async fn handler(
identity: Identity,
cookie: IdentityCookie,
Json(request): Json<Request>,
-) -> Result<(IdentityCookie, Json<Login>), Error> {
+) -> Result<(IdentityCookie, Json<User>), Error> {
let (login, secret) = app
.tokens()
- .change_password(&identity.login, &request.password, &request.to, &now)
+ .change_password(&identity.user, &request.password, &request.to, &now)
.await
.map_err(Error)?;
let cookie = cookie.set(secret);
diff --git a/src/login/routes/password/test.rs b/src/user/routes/password/test.rs
index c1974bf..f977327 100644
--- a/src/login/routes/password/test.rs
+++ b/src/user/routes/password/test.rs
@@ -10,13 +10,13 @@ use crate::{
async fn password_change() {
// Set up the environment
let app = fixtures::scratch_app().await;
- let creds = fixtures::login::create_with_password(&app, &fixtures::now()).await;
+ let creds = fixtures::user::create_with_password(&app, &fixtures::now()).await;
let cookie = fixtures::cookie::logged_in(&app, &creds, &fixtures::now()).await;
let identity = fixtures::identity::from_cookie(&app, &cookie, &fixtures::now()).await;
// Call the endpoint
let (name, password) = creds;
- let to = fixtures::login::propose_password();
+ let to = fixtures::user::propose_password();
let request = post::Request {
password: password.clone(),
to: to.clone(),
@@ -35,7 +35,7 @@ async fn password_change() {
assert_ne!(cookie.secret(), new_cookie.secret());
// Verify that we're still ourselves
- assert_eq!(identity.login, response);
+ assert_eq!(identity.user, response);
// Verify that our original token is no longer valid
let validate_err = app
@@ -64,5 +64,5 @@ async fn password_change() {
.login(&name, &to, &fixtures::now())
.await
.expect("logging in with the new password should succeed");
- assert_eq!(identity.login, login);
+ assert_eq!(identity.user, login);
}
diff --git a/src/login/snapshot.rs b/src/user/snapshot.rs
index 5c5dce0..d548e06 100644
--- a/src/login/snapshot.rs
+++ b/src/user/snapshot.rs
@@ -5,11 +5,11 @@ use super::{
use crate::name::Name;
// This also implements FromRequestParts (see `./extract.rs`). As a result, it
-// can be used as an extractor for endpoints that want to require login, or for
+// can be used as an extractor for endpoints that want to require a user, or for
// endpoints that need to behave differently depending on whether the client is
// or is not logged in.
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
-pub struct Login {
+pub struct User {
pub id: Id,
pub name: Name,
// The omission of the hashed password is deliberate, to minimize the
@@ -17,8 +17,9 @@ pub struct Login {
// of logic elsewhere.
}
-impl Login {
- // Two reasons for this allow:
+impl User {
+ // Without this allow, clippy wants the `Option<Self>` return type to be `Self`. It's not a bad
+ // suggestion, but we need `Option` here, for two reasons:
//
// 1. This is used to collect streams using a fold, below, which requires a type
// consistent with the fold, and
@@ -32,20 +33,20 @@ impl Login {
}
}
-impl FromIterator<Event> for Option<Login> {
+impl FromIterator<Event> for Option<User> {
fn from_iter<I: IntoIterator<Item = Event>>(events: I) -> Self {
- events.into_iter().fold(None, Login::apply)
+ events.into_iter().fold(None, User::apply)
}
}
-impl From<&Created> for Login {
+impl From<&Created> for User {
fn from(event: &Created) -> Self {
- event.login.clone()
+ event.user.clone()
}
}
-impl From<Created> for Login {
+impl From<Created> for User {
fn from(event: Created) -> Self {
- event.login
+ event.user
}
}
diff --git a/src/login/validate.rs b/src/user/validate.rs
index 0c97293..0c97293 100644
--- a/src/login/validate.rs
+++ b/src/user/validate.rs
diff --git a/ui/lib/apiServer.js b/ui/lib/apiServer.js
index c65b743..0b51883 100644
--- a/ui/lib/apiServer.js
+++ b/ui/lib/apiServer.js
@@ -45,9 +45,9 @@ export async function getInvite(inviteId) {
return apiServer.get(`/invite/${inviteId}`);
}
-export async function acceptInvite(inviteId, username, password) {
+export async function acceptInvite(inviteId, name, password) {
const data = {
- name: username,
+ name,
password
};
return apiServer.post(`/invite/${inviteId}`, data);
diff --git a/ui/lib/session.svelte.js b/ui/lib/session.svelte.js
index 16c2a98..67155ab 100644
--- a/ui/lib/session.svelte.js
+++ b/ui/lib/session.svelte.js
@@ -8,18 +8,18 @@ class Session {
remote = $state();
local = $state();
currentUser = $derived(this.remote.currentUser);
- logins = $derived(this.remote.logins.all);
+ users = $derived(this.remote.users.all);
channels = $derived(this.remote.channels.all);
messages = $derived(
this.remote.messages.all.map((message) =>
- message.resolve({ sender: (id) => this.logins.get(id) })
+ message.resolve({ sender: (id) => this.users.get(id) })
)
);
- static boot({ login, logins, channels, messages, resume_point }) {
+ static boot({ user, users, channels, messages, resume_point }) {
const remote = r.State.boot({
- currentUser: login,
- logins,
+ currentUser: user,
+ users,
channels,
messages,
resumePoint: resume_point
diff --git a/ui/lib/state/remote/state.svelte.js b/ui/lib/state/remote/state.svelte.js
index c4daf17..6cbe124 100644
--- a/ui/lib/state/remote/state.svelte.js
+++ b/ui/lib/state/remote/state.svelte.js
@@ -1,26 +1,26 @@
-import { Logins } from './logins.svelte.js';
+import { Users } from './users.svelte.js';
import { Channels } from './channels.svelte.js';
import { Messages } from './messages.svelte.js';
export class State {
currentUser = $state();
- logins = $state();
+ users = $state();
channels = $state();
messages = $state();
- static boot({ currentUser, logins, channels, messages, resumePoint }) {
+ static boot({ currentUser, users, channels, messages, resumePoint }) {
return new State({
currentUser,
- logins: Logins.boot(logins),
+ users: Users.boot(users),
channels: Channels.boot(channels),
messages: Messages.boot(messages),
resumePoint
});
}
- constructor({ currentUser, logins, channels, messages, resumePoint }) {
+ constructor({ currentUser, users, channels, messages, resumePoint }) {
this.currentUser = currentUser;
- this.logins = logins;
+ this.users = users;
this.channels = channels;
this.messages = messages;
this.resumePoint = resumePoint;
@@ -30,8 +30,8 @@ export class State {
switch (event.type) {
case 'channel':
return this.onChannelEvent(event);
- case 'login':
- return this.onLoginEvent(event);
+ case 'user':
+ return this.onUserEvent(event);
case 'message':
return this.onMessageEvent(event);
}
@@ -56,16 +56,16 @@ export class State {
this.channels.remove(id);
}
- onLoginEvent(event) {
+ onUserEvent(event) {
switch (event.event) {
case 'created':
- return this.onLoginCreated(event);
+ return this.onUserCreated(event);
}
}
- onLoginCreated(event) {
+ onUserCreated(event) {
const { id, name } = event;
- this.logins.add({ id, name });
+ this.users.add({ id, name });
}
onMessageEvent(event) {
diff --git a/ui/lib/state/remote/logins.svelte.js b/ui/lib/state/remote/users.svelte.js
index d19068d..617084f 100644
--- a/ui/lib/state/remote/logins.svelte.js
+++ b/ui/lib/state/remote/users.svelte.js
@@ -1,11 +1,11 @@
import { SvelteMap } from 'svelte/reactivity';
-export class Logins {
+export class Users {
all = $state();
- static boot(logins) {
- const all = new SvelteMap(logins.map((login) => [login.id, login]));
- return new Logins({ all });
+ static boot(users) {
+ const all = new SvelteMap(users.map((user) => [user.id, user]));
+ return new Users({ all });
}
constructor({ all }) {