1use std::fmt::Debug;
2
3use aok::{OK, Result};
4use xkv::{
5 R,
6 fred::interfaces::{KeysInterface, SortedSetsInterface},
7};
8use xbin::concat;
9use cookie_b::Browser;
10
11use crate::{Ctx, Extract};
12
13pub struct User {
14 pub id: u64,
15 pub bin: Box<[u8]>,
16}
17
18impl User {
19 pub fn new(id: u64) -> Self {
20 Self {
21 id,
22 bin: intbin::u64_bin(id),
23 }
24 }
25}
26
27impl Debug for User {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 f.write_str(format!("User:{}", self.id).as_str())
30 }
31}
32
33pub const COOKIE_EXPIRE: i64 = 86400 * 400;
35
36impl Extract for User {
37 async fn from_ctx(ctx: &Ctx) -> Result<Self> {
38 let bin: Box<[u8]>;
39 let id;
40
41 let req = &ctx.req;
42
43 #[allow(clippy::never_loop)]
44 loop {
45 if let Some(uid) = req.headers.get("content-type")
46 && let Ok(uid) = uid.to_str()
47 && uid != "#"
48 && let Ok(uid_bin) = ub64::b64d(uid)
49 && let Some(browser) = req.extensions.get::<Browser>()
50 {
51 let key = concat([b"bU:", &browser.bin[..]]);
58
59 let score: Option<i64> = R.zscore(&key[..], &uid_bin[..]).await?;
60
61 if let Some(score) = score
62 && score > 0
63 {
64 bin = uid_bin.into();
65 id = intbin::bin_u64(&bin);
66 if browser.renew {
67 tokio::spawn(async move {
68 R!(expire & key[..], COOKIE_EXPIRE, None);
69 OK
70 });
71 }
72 break;
73 }
74 }
75 bin = Box::new([]);
76 id = 0;
77 break;
78 }
79
80 Ok(Self { id, bin })
81 }
82}