1use std::time::Duration;
7
8use candid::{CandidType, Principal};
9use derive_new::new;
10use serde::{Deserialize, Serialize};
11use strum::{EnumIter, IntoStaticStr};
12use thiserror::Error;
13
14type TaskId = u64;
15type UtcTimestamp = u64;
16
17pub const CERTIFICATE_VALIDITY_FRACTION: f64 = 0.66;
21
22pub const CERT_EXPIRATION_ALERT_THRESHOLD: f64 = 0.2;
24
25pub const TASK_TIMEOUT: Duration = Duration::from_secs(10 * 60);
29
30pub const UNREGISTERED_DOMAIN_EXPIRATION_TIME: Duration = Duration::from_secs(24 * 60 * 60);
32
33pub const EXPIRED_DOMAIN_EXPIRATION_TIME: Duration = Duration::from_secs(7 * 24 * 60 * 60);
35
36pub const MAX_TASK_FAILURES: u32 = 20;
39
40pub const MIN_TASK_RETRY_DELAY: Duration = Duration::from_secs(30);
42
43pub const DEFAULT_PAGE_LIMIT: u32 = 100;
45
46pub const MAX_PAGE_LIMIT: u32 = 400;
48
49pub const STALE_DOMAINS_CLEANUP_INTERVAL: Duration = Duration::from_secs(3 * 60 * 60);
51
52pub type FetchTaskResult = Result<Option<ScheduledTask>, FetchTaskError>;
53pub type SubmitTaskResult = Result<(), SubmitTaskError>;
54pub type TryAddTaskResult = Result<(), TryAddTaskError>;
55pub type GetDomainStatusResult = Result<Option<DomainStatus>, GetDomainStatusError>;
56pub type GetDomainEntryResult = Result<Option<DomainEntry>, GetDomainEntryError>;
57pub type GetLastChangeTimeResult = Result<UtcTimestamp, GetLastChangeTimeError>;
58pub type ListCertificatesPageResult = Result<CertificatesPage, ListCertificatesPageError>;
59pub type ListDomainsPageResult = Result<DomainsPage, ListDomainsPageError>;
60pub type HasNextTaskResult = Result<bool, HasNextTaskError>;
61
62#[derive(CandidType, Deserialize, Serialize, Clone, Debug)]
63pub struct InitArg {
64 pub authorized_principal: Option<Principal>,
65}
66
67#[derive(
68 CandidType, Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq, Hash, IntoStaticStr,
69)]
70#[strum(serialize_all = "snake_case")]
71pub enum TaskKind {
72 Issue,
73 Renew,
74 Update,
75 Delete,
76}
77
78#[derive(CandidType, Deserialize, Serialize, Clone, Debug)]
79pub struct InputTask {
80 pub kind: TaskKind,
81 pub domain: String,
82}
83
84#[derive(CandidType, Deserialize, Serialize, Debug, Clone, PartialEq, Eq, new)]
85pub struct ScheduledTask {
86 pub kind: TaskKind,
87 pub domain: String,
88 pub id: TaskId,
89 pub enc_cert: Option<Vec<u8>>,
90}
91
92#[derive(CandidType, Deserialize, Serialize, Clone, Debug)]
93pub struct TaskResult {
94 pub domain: String,
95 pub outcome: TaskOutcome,
96 pub task_id: TaskId,
97 pub task_kind: TaskKind,
98 pub duration_secs: u64,
99}
100
101#[derive(CandidType, Deserialize, Serialize, Clone, Debug)]
102pub enum TaskOutcome {
103 Success(TaskOutput),
104 Failure(TaskFailReason),
105}
106
107#[derive(CandidType, Deserialize, Serialize, Clone, Debug)]
108pub enum TaskOutput {
109 Issue(IssueCertificateOutput),
110 Update(Principal),
111 Delete,
112}
113
114#[derive(CandidType, Deserialize, Serialize, Clone, Debug)]
115pub struct IssueCertificateOutput {
116 pub canister_id: Principal,
117 pub enc_cert: Vec<u8>,
118 pub enc_priv_key: Vec<u8>,
119 pub not_before: UtcTimestamp,
120 pub not_after: UtcTimestamp,
121}
122
123#[derive(CandidType, Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Error, IntoStaticStr)]
124#[strum(serialize_all = "snake_case")]
125pub enum TaskFailReason {
126 #[error("validation_failed: {0}")]
127 ValidationFailed(String),
128 #[error("timeout after {duration_secs}s")]
129 Timeout { duration_secs: UtcTimestamp },
130 #[error("rate_limited")]
131 RateLimited,
132 #[error("generic_failure: {0}")]
133 GenericFailure(String),
134}
135
136#[derive(CandidType, Clone, Deserialize, Serialize, Debug, PartialEq, Eq)]
137pub struct DomainStatus {
138 pub domain: String,
139 pub canister_id: Option<Principal>,
140 pub status: RegistrationStatus,
141}
142
143#[derive(CandidType, Clone, Deserialize, Serialize, Debug, PartialEq, Eq)]
144pub struct DomainEntry {
145 pub task: Option<TaskKind>,
146 pub last_fail_time: Option<UtcTimestamp>,
148 pub last_failure_reason: Option<TaskFailReason>,
150 pub failures_count: u32,
152 pub rate_limit_failures_count: u32,
154 pub canister_id: Option<Principal>,
156 pub created_at: UtcTimestamp,
158 pub taken_at: Option<UtcTimestamp>,
160 pub task_created_at: Option<UtcTimestamp>,
162 pub enc_cert: Option<Vec<u8>>,
164 pub enc_priv_key: Option<Vec<u8>>,
166 pub not_before: Option<UtcTimestamp>,
168 pub not_after: Option<UtcTimestamp>,
170}
171
172#[derive(CandidType, Clone, Deserialize, Serialize, Debug, PartialEq, Eq)]
174pub struct ListedDomainEntry {
175 pub domain: String,
177 pub task: Option<TaskKind>,
178 pub last_fail_time: Option<UtcTimestamp>,
179 pub last_failure_reason: Option<TaskFailReason>,
180 pub failures_count: u32,
181 pub rate_limit_failures_count: u32,
182 pub canister_id: Option<Principal>,
183 pub created_at: UtcTimestamp,
184 pub taken_at: Option<UtcTimestamp>,
185 pub task_created_at: Option<UtcTimestamp>,
186 pub not_before: Option<UtcTimestamp>,
187 pub not_after: Option<UtcTimestamp>,
188}
189
190#[derive(
191 CandidType, Clone, Deserialize, Serialize, Debug, EnumIter, IntoStaticStr, PartialEq, Eq,
192)]
193#[strum(serialize_all = "snake_case")]
194pub enum RegistrationStatus {
195 Registering,
197 Registered,
199 Expired,
201 Failed(String),
203}
204
205#[derive(CandidType, Clone, Deserialize, Serialize, Debug)]
206pub struct CertificatesPage {
207 pub items: Vec<RegisteredDomain>,
208 pub next_key: Option<String>,
209}
210
211impl CertificatesPage {
212 pub fn new(items: Vec<RegisteredDomain>, next_key: Option<String>) -> Self {
213 Self { items, next_key }
214 }
215}
216
217#[derive(CandidType, Clone, Deserialize, Serialize, Debug)]
218pub struct ListCertificatesPageInput {
219 pub start_key: Option<String>,
221 pub limit: Option<u32>,
223}
224
225impl ListCertificatesPageInput {
226 pub fn new() -> Self {
227 Self {
228 start_key: None,
229 limit: None,
230 }
231 }
232}
233
234impl Default for ListCertificatesPageInput {
235 fn default() -> Self {
236 Self::new()
237 }
238}
239
240#[derive(CandidType, Clone, Deserialize, Serialize, Debug)]
241pub struct RegisteredDomain {
242 pub domain: String,
243 pub canister_id: Principal,
244 pub enc_cert: Vec<u8>,
245 pub enc_priv_key: Vec<u8>,
246}
247
248#[derive(CandidType, Clone, Deserialize, Serialize, Debug)]
250pub struct DomainsPage {
251 pub items: Vec<ListedDomainEntry>,
252 pub next_key: Option<String>,
253}
254
255impl DomainsPage {
256 pub fn new(items: Vec<ListedDomainEntry>, next_key: Option<String>) -> Self {
257 Self { items, next_key }
258 }
259}
260
261#[derive(CandidType, Clone, Deserialize, Serialize, Debug)]
263pub struct ListDomainsPageInput {
264 pub start_key: Option<String>,
266 pub limit: Option<u32>,
268}
269
270impl ListDomainsPageInput {
271 pub fn new() -> Self {
272 Self {
273 start_key: None,
274 limit: None,
275 }
276 }
277}
278
279impl Default for ListDomainsPageInput {
280 fn default() -> Self {
281 Self::new()
282 }
283}
284
285#[derive(CandidType, Deserialize, Serialize, Debug, Clone, Error)]
286pub enum ListDomainsPageError {
287 #[error("Unauthorized")]
288 Unauthorized,
289 #[error("Internal error: {0}")]
290 InternalError(String),
291}
292
293#[derive(CandidType, Deserialize, Serialize, Debug, Clone, Error)]
294pub enum GetLastChangeTimeError {
295 #[error("Unauthorized")]
296 Unauthorized,
297 #[error("Internal error: {0}")]
298 InternalError(String),
299}
300
301#[derive(CandidType, Deserialize, Serialize, Debug, Clone, IntoStaticStr, Error)]
302#[strum(serialize_all = "snake_case")]
303pub enum FetchTaskError {
304 #[error("Unauthorized")]
305 Unauthorized,
306 #[error("Internal error: {0}")]
307 InternalError(String),
308}
309
310#[derive(CandidType, Deserialize, Serialize, Debug, Clone, Error)]
311pub enum GetDomainStatusError {
312 #[error("Unauthorized")]
313 Unauthorized,
314 #[error("Internal error: {0}")]
315 InternalError(String),
316}
317
318#[derive(CandidType, Deserialize, Serialize, Debug, Clone, Error)]
319pub enum GetDomainEntryError {
320 #[error("Unauthorized")]
321 Unauthorized,
322 #[error("Internal error: {0}")]
323 InternalError(String),
324}
325
326#[derive(CandidType, Deserialize, Serialize, Debug, Clone, Error)]
327pub enum ListCertificatesPageError {
328 #[error("Unauthorized")]
329 Unauthorized,
330 #[error("Internal error: {0}")]
331 InternalError(String),
332}
333
334#[derive(CandidType, Deserialize, Serialize, Debug, Clone, IntoStaticStr, Error, PartialEq, Eq)]
335#[strum(serialize_all = "snake_case")]
336pub enum SubmitTaskError {
337 #[error("Unauthorized")]
338 Unauthorized,
339 #[error("Domain not found: {0}")]
340 DomainNotFound(String),
341 #[error("A non-existing task was submitted: {0}")]
342 NonExistingTaskSubmitted(TaskId),
343 #[error("Internal error: {0}")]
344 InternalError(String),
345}
346
347#[derive(CandidType, Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Error)]
348pub enum HasNextTaskError {
349 #[error("Unauthorized")]
350 Unauthorized,
351 #[error("Internal error: {0}")]
352 InternalError(String),
353}
354
355#[derive(CandidType, Deserialize, Serialize, Debug, Clone, IntoStaticStr, Error)]
356#[strum(serialize_all = "snake_case")]
357pub enum TryAddTaskError {
358 #[error("Unauthorized")]
359 Unauthorized,
360 #[error("Domain not found: {0}")]
361 DomainNotFound(String),
362 #[error("Another task is already in progress for domain: {0}")]
363 AnotherTaskInProgress(String),
364 #[error("Certificate already issued for domain: {0}")]
365 CertificateAlreadyIssued(String),
366 #[error("Update requires an existing certificate: {0}")]
367 MissingCertificateForUpdate(String),
368 #[error("Internal error: {0}")]
369 InternalError(String),
370}