Skip to main content

wekan_cli/client/users/
channels.rs

1//! Stored v12.57 account channels. Response secrets become presence flags at decode.
2use serde::{Deserialize, Deserializer, Serialize};
3
4fn date<'de, D: Deserializer<'de>>(d: D) -> Result<String, D::Error> {
5    let value = String::deserialize(d)?;
6    crate::client::datetime::validate_response_date(&value)
7        .map_err(|()| serde::de::Error::custom("invalid account-channel timestamp"))?;
8    Ok(value)
9}
10fn optional_date<'de, D: Deserializer<'de>>(d: D) -> Result<Option<String>, D::Error> {
11    date(d).map(Some)
12}
13
14#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
15#[serde(transparent)]
16pub struct SecretPresence(bool);
17impl<'de> Deserialize<'de> for SecretPresence {
18    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
19        String::deserialize(d).map(|value| Self(!value.is_empty()))
20    }
21}
22
23/// server/passkeys.js stores the public key as base64url text.
24#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
25#[serde(
26    deny_unknown_fields,
27    rename_all(deserialize = "camelCase", serialize = "snake_case")
28)]
29pub struct Passkey {
30    pub id: String,
31    pub public_key: String,
32    pub counter: u64,
33    pub transports: Vec<String>,
34    pub name: String,
35    #[serde(deserialize_with = "date")]
36    pub created_at: String,
37    #[serde(default, deserialize_with = "optional_date")]
38    pub last_used_at: Option<String>,
39}
40
41/// Public SQRL identity keys and options, from server/sqrl.js.
42#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
43#[serde(
44    deny_unknown_fields,
45    rename_all(deserialize = "camelCase", serialize = "snake_case")
46)]
47pub struct SqrlIdentity {
48    pub idk: String,
49    pub suk: Option<String>,
50    pub vuk: Option<String>,
51    pub disabled: bool,
52    #[serde(deserialize_with = "date")]
53    pub added_at: String,
54    pub sqrl_only: Option<bool>,
55    pub hardlock: Option<bool>,
56}
57
58/// models/lib/webPush.js normalizes subscriptions before storing them.
59#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
60#[serde(
61    deny_unknown_fields,
62    rename_all(deserialize = "camelCase", serialize = "snake_case")
63)]
64pub struct PushSubscription {
65    #[serde(rename(deserialize = "endpoint", serialize = "endpoint_present"))]
66    pub endpoint: SecretPresence,
67    pub keys: PushKeys,
68    #[serde(deserialize_with = "date")]
69    pub created_at: String,
70}
71#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
72#[serde(deny_unknown_fields)]
73pub struct PushKeys {
74    pub p256dh: String,
75    #[serde(rename(deserialize = "auth", serialize = "auth_present"))]
76    pub auth: SecretPresence,
77}
78
79/// models/lib/ntfy.js stores a topic URL and optional access token.
80#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
81#[serde(deny_unknown_fields)]
82pub struct NtfySettings {
83    pub enabled: bool,
84    #[serde(rename(deserialize = "url", serialize = "url_present"))]
85    pub url: SecretPresence,
86    #[serde(rename(deserialize = "token", serialize = "token_present"))]
87    pub token: SecretPresence,
88}
89
90/// server/lib/trayDelivery.js's bounded pending receipt.
91#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
92#[serde(
93    deny_unknown_fields,
94    rename_all(deserialize = "camelCase", serialize = "snake_case")
95)]
96pub struct PendingNotification {
97    #[serde(rename(deserialize = "_id", serialize = "receipt_id"))]
98    pub receipt_id: String,
99    pub version: u64,
100    pub user_id: String,
101    pub activity_id: String,
102}