Commit
+144 -58 +/-5 browse
1 | diff --git a/ayllu-mail/src/config.rs b/ayllu-mail/src/config.rs |
2 | index a6ef261..a324b05 100644 |
3 | --- a/ayllu-mail/src/config.rs |
4 | +++ b/ayllu-mail/src/config.rs |
5 | @@ -21,12 +21,39 @@ impl Database { |
6 | } |
7 | |
8 | #[derive(Deserialize, Serialize, Clone, Debug, Default)] |
9 | + pub enum PostPolicy { |
10 | + AnnounceOnly, |
11 | + SubscriptionOnly, |
12 | + ApprovalNeeded, |
13 | + Open, |
14 | + #[default] |
15 | + Custom, |
16 | + } |
17 | + |
18 | + #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq, Eq)] |
19 | + pub enum SubscriptionPolicyKind { |
20 | + Request, |
21 | + Open, |
22 | + Manual, |
23 | + #[default] |
24 | + Custom, |
25 | + } |
26 | + |
27 | + #[derive(Deserialize, Serialize, Clone, Debug, Default)] |
28 | + pub struct SubscriptionPolicy { |
29 | + pub send_confirmation: bool, |
30 | + pub kind: SubscriptionPolicyKind, |
31 | + } |
32 | + |
33 | + #[derive(Deserialize, Serialize, Clone, Debug, Default)] |
34 | pub struct MailingList { |
35 | pub id: String, |
36 | pub name: Option<String>, |
37 | pub address: String, |
38 | pub description: Option<String>, |
39 | pub topics: Vec<String>, |
40 | + pub post_policy: PostPolicy, |
41 | + pub subscription_policy: SubscriptionPolicy, |
42 | } |
43 | |
44 | #[derive(Serialize, Deserialize, Clone)] |
45 | diff --git a/ayllu-mail/src/declarative.rs b/ayllu-mail/src/declarative.rs |
46 | new file mode 100644 |
47 | index 0000000..0708e12 |
48 | --- /dev/null |
49 | +++ b/ayllu-mail/src/declarative.rs |
50 | @@ -0,0 +1,104 @@ |
51 | + use std::collections::HashMap; |
52 | + |
53 | + use mailpot::{ |
54 | + models::{MailingList, PostPolicy, SubscriptionPolicy}, |
55 | + Connection, Result as MpResult, |
56 | + }; |
57 | + use tracing::log; |
58 | + |
59 | + use crate::{config, config::Config}; |
60 | + |
61 | + /// ensure the mailing lists in the global config are available |
62 | + pub fn initialize(cfg: &Config) -> MpResult<()> { |
63 | + let db = Connection::open_or_create_db(cfg.mailpot_config())?.trusted(); |
64 | + |
65 | + let mut lists_by_name: HashMap<String, i64> = HashMap::new(); |
66 | + let mut managed_lists: Vec<String> = Vec::new(); |
67 | + |
68 | + for list in db.lists()? { |
69 | + lists_by_name.insert(list.name.clone(), list.pk); |
70 | + } |
71 | + |
72 | + // create missing lists |
73 | + |
74 | + for list in cfg.mail.lists.iter() { |
75 | + log::info!("configuring mailing list: {} [{}]", list.id, list.address); |
76 | + // TODO: confused on distinciton between id and name |
77 | + if !lists_by_name.contains_key(&list.id) { |
78 | + log::info!("creating mailing list: {}", list.address); |
79 | + let db_list = db.create_list(MailingList { |
80 | + pk: -1, |
81 | + name: list.name.clone().unwrap_or(list.id.clone()), |
82 | + id: list.id.clone(), |
83 | + address: list.address.clone(), |
84 | + topics: list.topics.clone(), |
85 | + description: list.description.clone(), |
86 | + archive_url: None, |
87 | + })?; |
88 | + lists_by_name.insert(db_list.id.clone(), db_list.pk); |
89 | + } |
90 | + managed_lists.push(list.id.clone()); |
91 | + } |
92 | + |
93 | + // set policies |
94 | + |
95 | + for list in cfg.mail.lists.iter() { |
96 | + let list_pk = *lists_by_name.get(&list.id).unwrap(); |
97 | + let post_policy = PostPolicy { |
98 | + pk: 0, // TODO ? |
99 | + list: list_pk, |
100 | + announce_only: matches!(list.post_policy, config::PostPolicy::AnnounceOnly), |
101 | + subscription_only: matches!(list.post_policy, config::PostPolicy::SubscriptionOnly), |
102 | + approval_needed: matches!(list.post_policy, config::PostPolicy::ApprovalNeeded), |
103 | + open: matches!(list.post_policy, config::PostPolicy::Open), |
104 | + custom: matches!(list.post_policy, config::PostPolicy::Custom), |
105 | + }; |
106 | + log::info!("setting post policy: {:?}", list.post_policy); |
107 | + db.set_list_post_policy(post_policy)?; |
108 | + |
109 | + let sub_policy = SubscriptionPolicy { |
110 | + pk: 0, |
111 | + list: list_pk, |
112 | + send_confirmation: list.subscription_policy.send_confirmation, |
113 | + open: matches!( |
114 | + list.subscription_policy.kind, |
115 | + config::SubscriptionPolicyKind::Open |
116 | + ), |
117 | + manual: matches!( |
118 | + list.subscription_policy.kind, |
119 | + config::SubscriptionPolicyKind::Manual |
120 | + ), |
121 | + request: matches!( |
122 | + list.subscription_policy.kind, |
123 | + config::SubscriptionPolicyKind::Request |
124 | + ), |
125 | + custom: matches!( |
126 | + list.subscription_policy.kind, |
127 | + config::SubscriptionPolicyKind::Custom |
128 | + ), |
129 | + }; |
130 | + |
131 | + log::info!( |
132 | + "setting subscription policy: {:?}", |
133 | + list.subscription_policy |
134 | + ); |
135 | + |
136 | + db.set_list_subscription_policy(sub_policy)?; |
137 | + } |
138 | + |
139 | + let extras: Vec<&String> = lists_by_name |
140 | + .keys() |
141 | + .into_iter() |
142 | + .filter(|name| managed_lists.contains(name) == false) |
143 | + .collect(); |
144 | + |
145 | + if extras.len() > 0 { |
146 | + // TODO: there is no way to delete lists currently |
147 | + log::info!("database contains the following superfluous lists:"); |
148 | + extras.iter().for_each(|extra| { |
149 | + log::info!("List: {}", extra); |
150 | + }); |
151 | + } |
152 | + |
153 | + Ok(()) |
154 | + } |
155 | diff --git a/ayllu-mail/src/main.rs b/ayllu-mail/src/main.rs |
156 | index 0a33215..d0a90bc 100644 |
157 | --- a/ayllu-mail/src/main.rs |
158 | +++ b/ayllu-mail/src/main.rs |
159 | @@ -1,45 +1,12 @@ |
160 | use std::path::Path; |
161 | |
162 | use clap::{arg, value_parser, Command}; |
163 | - use mailpot::{models::MailingList, Connection, Result as MpResult}; |
164 | use tokio::{runtime::Builder as TokioBuilder, task::LocalSet}; |
165 | |
166 | - use tracing::log; |
167 | - |
168 | - |
169 | mod config; |
170 | + mod declarative; |
171 | mod server; |
172 | |
173 | - /// ensure the mailing lists in the global config are available |
174 | - pub fn initialize(cfg: &config::Config) -> MpResult<()> { |
175 | - let db = Connection::open_or_create_db(cfg.mailpot_config())?.trusted(); |
176 | - // initialize any mailing list which hasn't been created |
177 | - let existing_lists: Vec<String> = db |
178 | - .lists()? |
179 | - .iter() |
180 | - .map(|list| list.address.clone()) |
181 | - .collect(); |
182 | - |
183 | - for mailing_list in cfg.mail.lists.clone() { |
184 | - if !existing_lists |
185 | - .iter().any(|address| *address == mailing_list.address) |
186 | - { |
187 | - log::info!("creating mailing list: {}", mailing_list.address); |
188 | - db.create_list(MailingList { |
189 | - pk: -1, |
190 | - name: mailing_list.name.unwrap_or(mailing_list.id.clone()), |
191 | - id: mailing_list.id, |
192 | - address: mailing_list.address, |
193 | - topics: mailing_list.topics, |
194 | - description: mailing_list.description, |
195 | - archive_url: None, |
196 | - })?; |
197 | - } |
198 | - } |
199 | - |
200 | - Ok(()) |
201 | - } |
202 | - |
203 | fn main() -> Result<(), Box<dyn std::error::Error>> { |
204 | let command = Command::new("ayllu-mail") |
205 | .about("ayllu email service") |
206 | @@ -56,12 +23,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { |
207 | .value_parser(value_parser!(String)), |
208 | ); |
209 | let matches = command.get_matches(); |
210 | - let config_path = matches |
211 | - .get_one::<String>("config") |
212 | - .map(Path::new); |
213 | + let config_path = matches.get_one::<String>("config").map(Path::new); |
214 | let ayllu_config = config::load(config_path)?; |
215 | let log_level = matches |
216 | - .get_one::<String>("level").cloned() |
217 | + .get_one::<String>("level") |
218 | + .cloned() |
219 | .unwrap_or(ayllu_config.log_level.clone()); |
220 | tracing_subscriber::fmt() |
221 | .compact() |
222 | @@ -74,7 +40,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { |
223 | )) |
224 | .init(); |
225 | tracing::info!("logger initialized"); |
226 | - initialize(&ayllu_config)?; |
227 | + declarative::initialize(&ayllu_config)?; |
228 | TokioBuilder::new_current_thread() |
229 | .enable_all() |
230 | .build() |
231 | diff --git a/ayllu-mail/src/server.rs b/ayllu-mail/src/server.rs |
232 | index c98cf03..f21b111 100644 |
233 | --- a/ayllu-mail/src/server.rs |
234 | +++ b/ayllu-mail/src/server.rs |
235 | @@ -2,7 +2,7 @@ use std::collections::HashMap; |
236 | use std::error::Error as StdError; |
237 | use std::sync::{Arc, RwLock}; |
238 | |
239 | - use capnp::capability::Promise; |
240 | + use capnp::{capability::Promise, Error as CapnpError}; |
241 | use capnp_rpc::pry; |
242 | use mailpot::{models::Post, Connection}; |
243 | use melib::{thread::Threads, Envelope, EnvelopeHash}; |
244 | @@ -10,8 +10,7 @@ use tracing::log; |
245 | |
246 | use crate::config::Config; |
247 | use ayllu_api::mail_capnp::server::{ |
248 | - Client, ListThreadsParams, ListThreadsResults, PostMessageParams, PostMessageResults, |
249 | - ReadThreadParams, ReadThreadResults, Server, |
250 | + Client, ListThreadsParams, ListThreadsResults, ReadThreadParams, ReadThreadResults, Server, |
251 | }; |
252 | use ayllu_rpc::Server as RpcHelper; |
253 | |
254 | @@ -24,7 +23,7 @@ impl Server for ServerImpl { |
255 | &mut self, |
256 | params: ListThreadsParams, |
257 | mut results: ListThreadsResults, |
258 | - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { |
259 | + ) -> Promise<(), CapnpError> { |
260 | let mailing_list_name = pry!(pry!(pry!(params.get()).get_name()).to_string()); |
261 | log::info!("looking up threads: {}", mailing_list_name); |
262 | for list in self.db.lists().unwrap() { |
263 | @@ -71,23 +70,15 @@ impl Server for ServerImpl { |
264 | |
265 | fn read_thread( |
266 | &mut self, |
267 | - _: ReadThreadParams, |
268 | - _: ReadThreadResults, |
269 | - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { |
270 | + params: ReadThreadParams, |
271 | + mut results: ReadThreadResults, |
272 | + ) -> Promise<(), CapnpError> { |
273 | + let params = pry!(params.get()); |
274 | + let (thread_id, offset, limit) = (params.get_id(), params.get_offset(), params.get_limit()); |
275 | ::capnp::capability::Promise::err(::capnp::Error::unimplemented( |
276 | "method server::Server::read_thread not implemented".to_string(), |
277 | )) |
278 | } |
279 | - |
280 | - fn post_message( |
281 | - &mut self, |
282 | - _: PostMessageParams, |
283 | - _: PostMessageResults, |
284 | - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { |
285 | - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( |
286 | - "method server::Server::post_message not implemented".to_string(), |
287 | - )) |
288 | - } |
289 | } |
290 | |
291 | pub async fn serve(cfg: &Config) -> Result<(), Box<dyn StdError>> { |
292 | diff --git a/crates/api/v1/mail.capnp b/crates/api/v1/mail.capnp |
293 | index b87cd64..797f45d 100644 |
294 | --- a/crates/api/v1/mail.capnp |
295 | +++ b/crates/api/v1/mail.capnp |
296 | @@ -23,6 +23,4 @@ interface Server { |
297 | listThreads @0 (name: Text, offset: Int64, limit: Int64) -> (threads: List(Thread)); |
298 | # list all of the responses associated with a post |
299 | readThread @1 (id: Int64, offset: Int64, limit: Int64) -> (thread: List(Message)); |
300 | - # post a new message into mailpot |
301 | - postMessage @2 (message: Data) -> (id: Int64); |
302 | } |