max / goingson
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
8 files changed,
+269 insertions,
-216 deletions
| @@ -80,14 +80,14 @@ | |||
| 80 | 80 | Annotation, Attachment, AttachmentMeta, BackupSettings, BlockType, CssClass, DailyNote, | |
| 81 | 81 | DbValue, Email, EmailAccount, EmailAuthType, EmailThread, Event, FolderSyncState, Milestone, | |
| 82 | 82 | MilestoneStatus, MonthlyGoal, MonthlyGoalStatus, MonthlyReflection, MonthlySpec, NewAttachment, | |
| 83 | - | NewBackupSettings, NewEmail, NewEmailWithTracking, NewEvent, NewEventBuilder, NewMilestone, | |
| 84 | - | NewProblem, NewProject, NewSavedView, NewTask, NewTaskBuilder, ParseableEnum, PositiveMinutes, | |
| 85 | - | Priority, Problem, ProblemBand, ProblemStatus, Project, ProjectStatus, ProjectType, Recurrence, | |
| 86 | - | RecurrenceRule, SavedView, SortDirection, SortField, StatusToken, Subtask, SyncAccount, | |
| 87 | - | TOKEN_KIND_COMMIT, Task, TaskFilterQuery, TaskSortColumn, TaskStatus, TimeSession, | |
| 88 | - | TimeSummaryPanel, TimeSummaryProject, TimeTrackingSummary, TokenState, UpdateEvent, | |
| 89 | - | UpdateProject, UpdateTask, User, ViewFilters, ViewType, WeeklyReview, format_file_size, | |
| 90 | - | mime_from_extension, roll_up_time_summary, snap_all_day_span, | |
| 83 | + | NewBackupSettings, NewEmail, NewEmailAccount, NewEmailWithTracking, NewEvent, NewEventBuilder, | |
| 84 | + | NewMilestone, NewProblem, NewProject, NewSavedView, NewTask, NewTaskBuilder, ParseableEnum, | |
| 85 | + | PositiveMinutes, Priority, Problem, ProblemBand, ProblemStatus, Project, ProjectStatus, | |
| 86 | + | ProjectType, Recurrence, RecurrenceRule, SavedView, SortDirection, SortField, StatusToken, | |
| 87 | + | Subtask, SyncAccount, TOKEN_KIND_COMMIT, Task, TaskFilterQuery, TaskSortColumn, TaskStatus, | |
| 88 | + | TimeSession, TimeSummaryPanel, TimeSummaryProject, TimeTrackingSummary, TokenState, | |
| 89 | + | UpdateEmailAccount, UpdateEvent, UpdateProject, UpdateTask, User, ViewFilters, ViewType, | |
| 90 | + | WeeklyReview, format_file_size, mime_from_extension, roll_up_time_summary, snap_all_day_span, | |
| 91 | 91 | }; | |
| 92 | 92 | pub use parser::{ParseResult, ParsedTask, parse_quick_add, parse_quick_add_with_warnings}; | |
| 93 | 93 | pub use recurrence::{ |
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | mod common; | |
| 4 | 4 | ||
| 5 | 5 | use chrono::{Duration, Utc}; | |
| 6 | - | use goingson_core::{EmailAccountRepository, EmailAuthType}; | |
| 6 | + | use goingson_core::{EmailAccountRepository, EmailAuthType, NewEmailAccount, UpdateEmailAccount}; | |
| 7 | 7 | use goingson_db_sqlite::SqliteEmailAccountRepository; | |
| 8 | 8 | ||
| 9 | 9 | #[tokio::test] | |
| @@ -15,16 +15,18 @@ | |||
| 15 | 15 | let created = repo | |
| 16 | 16 | .create( | |
| 17 | 17 | user_id, | |
| 18 | - | "Work Email", | |
| 19 | - | "work@example.com", | |
| 20 | - | "imap.example.com", | |
| 21 | - | 993, | |
| 22 | - | "smtp.example.com", | |
| 23 | - | 587, | |
| 24 | - | "work@example.com", | |
| 25 | - | "secret123", | |
| 26 | - | true, | |
| 27 | - | Some("Archive"), | |
| 18 | + | NewEmailAccount { | |
| 19 | + | account_name: "Work Email", | |
| 20 | + | email_address: "work@example.com", | |
| 21 | + | imap_server: "imap.example.com", | |
| 22 | + | imap_port: 993, | |
| 23 | + | smtp_server: "smtp.example.com", | |
| 24 | + | smtp_port: 587, | |
| 25 | + | username: "work@example.com", | |
| 26 | + | password: "secret123", | |
| 27 | + | use_tls: true, | |
| 28 | + | archive_folder_name: Some("Archive"), | |
| 29 | + | }, | |
| 28 | 30 | ) | |
| 29 | 31 | .await | |
| 30 | 32 | .expect("Failed to create account"); | |
| @@ -142,32 +144,36 @@ | |||
| 142 | 144 | ||
| 143 | 145 | repo.create( | |
| 144 | 146 | user_id, | |
| 145 | - | "Beta Account", | |
| 146 | - | "beta@example.com", | |
| 147 | - | "imap.beta.com", | |
| 148 | - | 993, | |
| 149 | - | "smtp.beta.com", | |
| 150 | - | 587, | |
| 151 | - | "beta", | |
| 152 | - | "pass", | |
| 153 | - | true, | |
| 154 | - | None, | |
| 147 | + | NewEmailAccount { | |
| 148 | + | account_name: "Beta Account", | |
| 149 | + | email_address: "beta@example.com", | |
| 150 | + | imap_server: "imap.beta.com", | |
| 151 | + | imap_port: 993, | |
| 152 | + | smtp_server: "smtp.beta.com", | |
| 153 | + | smtp_port: 587, | |
| 154 | + | username: "beta", | |
| 155 | + | password: "pass", | |
| 156 | + | use_tls: true, | |
| 157 | + | archive_folder_name: None, | |
| 158 | + | }, | |
| 155 | 159 | ) | |
| 156 | 160 | .await | |
| 157 | 161 | .expect("Failed to create beta account"); | |
| 158 | 162 | ||
| 159 | 163 | repo.create( | |
| 160 | 164 | user_id, | |
| 161 | - | "Alpha Account", | |
| 162 | - | "alpha@example.com", | |
| 163 | - | "imap.alpha.com", | |
| 164 | - | 993, | |
| 165 | - | "smtp.alpha.com", | |
| 166 | - | 587, | |
| 167 | - | "alpha", | |
| 168 | - | "pass", | |
| 169 | - | true, | |
| 170 | - | None, | |
| 165 | + | NewEmailAccount { | |
| 166 | + | account_name: "Alpha Account", | |
| 167 | + | email_address: "alpha@example.com", | |
| 168 | + | imap_server: "imap.alpha.com", | |
| 169 | + | imap_port: 993, | |
| 170 | + | smtp_server: "smtp.alpha.com", | |
| 171 | + | smtp_port: 587, | |
| 172 | + | username: "alpha", | |
| 173 | + | password: "pass", | |
| 174 | + | use_tls: true, | |
| 175 | + | archive_folder_name: None, | |
| 176 | + | }, | |
| 171 | 177 | ) | |
| 172 | 178 | .await | |
| 173 | 179 | .expect("Failed to create alpha account"); | |
| @@ -190,16 +196,18 @@ | |||
| 190 | 196 | let created = repo | |
| 191 | 197 | .create( | |
| 192 | 198 | user_id, | |
| 193 | - | "Original", | |
| 194 | - | "old@example.com", | |
| 195 | - | "imap.old.com", | |
| 196 | - | 993, | |
| 197 | - | "smtp.old.com", | |
| 198 | - | 587, | |
| 199 | - | "olduser", | |
| 200 | - | "oldpass", | |
| 201 | - | true, | |
| 202 | - | None, | |
| 199 | + | NewEmailAccount { | |
| 200 | + | account_name: "Original", | |
| 201 | + | email_address: "old@example.com", | |
| 202 | + | imap_server: "imap.old.com", | |
| 203 | + | imap_port: 993, | |
| 204 | + | smtp_server: "smtp.old.com", | |
| 205 | + | smtp_port: 587, | |
| 206 | + | username: "olduser", | |
| 207 | + | password: "oldpass", | |
| 208 | + | use_tls: true, | |
| 209 | + | archive_folder_name: None, | |
| 210 | + | }, | |
| 203 | 211 | ) | |
| 204 | 212 | .await | |
| 205 | 213 | .expect("Failed to create account"); | |
| @@ -208,16 +216,18 @@ | |||
| 208 | 216 | .update( | |
| 209 | 217 | created.id, | |
| 210 | 218 | user_id, | |
| 211 | - | "Updated Name", | |
| 212 | - | "new@example.com", | |
| 213 | - | "imap.new.com", | |
| 214 | - | 995, | |
| 215 | - | "smtp.new.com", | |
| 216 | - | 465, | |
| 217 | - | "newuser", | |
| 218 | - | Some("newpass"), | |
| 219 | - | false, | |
| 220 | - | Some("All Mail"), | |
| 219 | + | UpdateEmailAccount { | |
| 220 | + | account_name: "Updated Name", | |
| 221 | + | email_address: "new@example.com", | |
| 222 | + | imap_server: "imap.new.com", | |
| 223 | + | imap_port: 995, | |
| 224 | + | smtp_server: "smtp.new.com", | |
| 225 | + | smtp_port: 465, | |
| 226 | + | username: "newuser", | |
| 227 | + | password: Some("newpass"), | |
| 228 | + | use_tls: false, | |
| 229 | + | archive_folder_name: Some("All Mail"), | |
| 230 | + | }, | |
| 221 | 231 | ) | |
| 222 | 232 | .await | |
| 223 | 233 | .expect("Failed to update account"); | |
| @@ -245,16 +255,18 @@ | |||
| 245 | 255 | let created = repo | |
| 246 | 256 | .create( | |
| 247 | 257 | user_id, | |
| 248 | - | "My Account", | |
| 249 | - | "me@example.com", | |
| 250 | - | "imap.example.com", | |
| 251 | - | 993, | |
| 252 | - | "smtp.example.com", | |
| 253 | - | 587, | |
| 254 | - | "me", | |
| 255 | - | "originalpass", | |
| 256 | - | true, | |
| 257 | - | None, | |
| 258 | + | NewEmailAccount { | |
| 259 | + | account_name: "My Account", | |
| 260 | + | email_address: "me@example.com", | |
| 261 | + | imap_server: "imap.example.com", | |
| 262 | + | imap_port: 993, | |
| 263 | + | smtp_server: "smtp.example.com", | |
| 264 | + | smtp_port: 587, | |
| 265 | + | username: "me", | |
| 266 | + | password: "originalpass", | |
| 267 | + | use_tls: true, | |
| 268 | + | archive_folder_name: None, | |
| 269 | + | }, | |
| 258 | 270 | ) | |
| 259 | 271 | .await | |
| 260 | 272 | .expect("Failed to create account"); | |
| @@ -263,16 +275,18 @@ | |||
| 263 | 275 | .update( | |
| 264 | 276 | created.id, | |
| 265 | 277 | user_id, | |
| 266 | - | "Renamed Account", | |
| 267 | - | "me@example.com", | |
| 268 | - | "imap.example.com", | |
| 269 | - | 993, | |
| 270 | - | "smtp.example.com", | |
| 271 | - | 587, | |
| 272 | - | "me", | |
| 273 | - | None, | |
| 274 | - | true, | |
| 275 | - | None, | |
| 278 | + | UpdateEmailAccount { | |
| 279 | + | account_name: "Renamed Account", | |
| 280 | + | email_address: "me@example.com", | |
| 281 | + | imap_server: "imap.example.com", | |
| 282 | + | imap_port: 993, | |
| 283 | + | smtp_server: "smtp.example.com", | |
| 284 | + | smtp_port: 587, | |
| 285 | + | username: "me", | |
| 286 | + | password: None, | |
| 287 | + | use_tls: true, | |
| 288 | + | archive_folder_name: None, | |
| 289 | + | }, | |
| 276 | 290 | ) | |
| 277 | 291 | .await | |
| 278 | 292 | .expect("Failed to update account"); | |
| @@ -378,16 +392,18 @@ | |||
| 378 | 392 | let created = repo | |
| 379 | 393 | .create( | |
| 380 | 394 | user_id, | |
| 381 | - | "Throwaway", | |
| 382 | - | "throw@example.com", | |
| 383 | - | "imap.example.com", | |
| 384 | - | 993, | |
| 385 | - | "smtp.example.com", | |
| 386 | - | 587, | |
| 387 | - | "throw", | |
| 388 | - | "pass", | |
| 389 | - | true, | |
| 390 | - | None, | |
| 395 | + | NewEmailAccount { | |
| 396 | + | account_name: "Throwaway", | |
| 397 | + | email_address: "throw@example.com", | |
| 398 | + | imap_server: "imap.example.com", | |
| 399 | + | imap_port: 993, | |
| 400 | + | smtp_server: "smtp.example.com", | |
| 401 | + | smtp_port: 587, | |
| 402 | + | username: "throw", | |
| 403 | + | password: "pass", | |
| 404 | + | use_tls: true, | |
| 405 | + | archive_folder_name: None, | |
| 406 | + | }, | |
| 391 | 407 | ) | |
| 392 | 408 | .await | |
| 393 | 409 | .expect("Failed to create account"); | |
| @@ -415,16 +431,18 @@ | |||
| 415 | 431 | let created = repo | |
| 416 | 432 | .create( | |
| 417 | 433 | user1, | |
| 418 | - | "User1 Email", | |
| 419 | - | "user1@example.com", | |
| 420 | - | "imap.example.com", | |
| 421 | - | 993, | |
| 422 | - | "smtp.example.com", | |
| 423 | - | 587, | |
| 424 | - | "user1", | |
| 425 | - | "pass", | |
| 426 | - | true, | |
| 427 | - | None, | |
| 434 | + | NewEmailAccount { | |
| 435 | + | account_name: "User1 Email", | |
| 436 | + | email_address: "user1@example.com", | |
| 437 | + | imap_server: "imap.example.com", | |
| 438 | + | imap_port: 993, | |
| 439 | + | smtp_server: "smtp.example.com", | |
| 440 | + | smtp_port: 587, | |
| 441 | + | username: "user1", | |
| 442 | + | password: "pass", | |
| 443 | + | use_tls: true, | |
| 444 | + | archive_folder_name: None, | |
| 445 | + | }, | |
| 428 | 446 | ) | |
| 429 | 447 | .await | |
| 430 | 448 | .expect("Failed to create account"); | |
| @@ -453,16 +471,18 @@ | |||
| 453 | 471 | let created = repo | |
| 454 | 472 | .create( | |
| 455 | 473 | user_id, | |
| 456 | - | "Sync Test", | |
| 457 | - | "sync@example.com", | |
| 458 | - | "imap.example.com", | |
| 459 | - | 993, | |
| 460 | - | "smtp.example.com", | |
| 461 | - | 587, | |
| 462 | - | "sync", | |
| 463 | - | "pass", | |
| 464 | - | true, | |
| 465 | - | None, | |
| 474 | + | NewEmailAccount { | |
| 475 | + | account_name: "Sync Test", | |
| 476 | + | email_address: "sync@example.com", | |
| 477 | + | imap_server: "imap.example.com", | |
| 478 | + | imap_port: 993, | |
| 479 | + | smtp_server: "smtp.example.com", | |
| 480 | + | smtp_port: 587, | |
| 481 | + | username: "sync", | |
| 482 | + | password: "pass", | |
| 483 | + | use_tls: true, | |
| 484 | + | archive_folder_name: None, | |
| 485 | + | }, | |
| 466 | 486 | ) | |
| 467 | 487 | .await | |
| 468 | 488 | .expect("Failed to create account"); | |
| @@ -492,16 +512,18 @@ | |||
| 492 | 512 | let created = repo | |
| 493 | 513 | .create( | |
| 494 | 514 | user_id, | |
| 495 | - | "Interval Test", | |
| 496 | - | "interval@example.com", | |
| 497 | - | "imap.example.com", | |
| 498 | - | 993, | |
| 499 | - | "smtp.example.com", | |
| 500 | - | 587, | |
| 501 | - | "interval", | |
| 502 | - | "pass", | |
| 503 | - | true, | |
| 504 | - | None, | |
| 515 | + | NewEmailAccount { | |
| 516 | + | account_name: "Interval Test", | |
| 517 | + | email_address: "interval@example.com", | |
| 518 | + | imap_server: "imap.example.com", | |
| 519 | + | imap_port: 993, | |
| 520 | + | smtp_server: "smtp.example.com", | |
| 521 | + | smtp_port: 587, | |
| 522 | + | username: "interval", | |
| 523 | + | password: "pass", | |
| 524 | + | use_tls: true, | |
| 525 | + | archive_folder_name: None, | |
| 526 | + | }, | |
| 505 | 527 | ) | |
| 506 | 528 | .await | |
| 507 | 529 | .expect("Failed to create account"); | |
| @@ -537,16 +559,18 @@ | |||
| 537 | 559 | let account = repo | |
| 538 | 560 | .create( | |
| 539 | 561 | user_id, | |
| 540 | - | "Auto Sync", | |
| 541 | - | "autosync@example.com", | |
| 542 | - | "imap.example.com", | |
| 543 | - | 993, | |
| 544 | - | "smtp.example.com", | |
| 545 | - | 587, | |
| 546 | - | "autosync", | |
| 547 | - | "pass", | |
| 548 | - | true, | |
| 549 | - | None, | |
| 562 | + | NewEmailAccount { | |
| 563 | + | account_name: "Auto Sync", | |
| 564 | + | email_address: "autosync@example.com", | |
| 565 | + | imap_server: "imap.example.com", | |
| 566 | + | imap_port: 993, | |
| 567 | + | smtp_server: "smtp.example.com", | |
| 568 | + | smtp_port: 587, | |
| 569 | + | username: "autosync", | |
| 570 | + | password: "pass", | |
| 571 | + | use_tls: true, | |
| 572 | + | archive_folder_name: None, | |
| 573 | + | }, | |
| 550 | 574 | ) | |
| 551 | 575 | .await | |
| 552 | 576 | .expect("Failed to create account"); |
| @@ -9,7 +9,9 @@ | |||
| 9 | 9 | ||
| 10 | 10 | use chrono::{Duration, Utc}; | |
| 11 | 11 | use goingson_core::email_sync::{FetchedEmail, process_fetched_emails}; | |
| 12 | - | use goingson_core::{EmailAccountId, EmailAccountRepository, EmailRepository, UserId}; | |
| 12 | + | use goingson_core::{ | |
| 13 | + | EmailAccountId, EmailAccountRepository, EmailRepository, NewEmailAccount, UserId, | |
| 14 | + | }; | |
| 13 | 15 | use goingson_db_sqlite::{SqliteEmailAccountRepository, SqliteEmailRepository}; | |
| 14 | 16 | use sqlx::SqlitePool; | |
| 15 | 17 | ||
| @@ -18,16 +20,18 @@ | |||
| 18 | 20 | let account = repo | |
| 19 | 21 | .create( | |
| 20 | 22 | user_id, | |
| 21 | - | "Test", | |
| 22 | - | "me@example.com", | |
| 23 | - | "imap.example.com", | |
| 24 | - | 993, | |
| 25 | - | "smtp.example.com", | |
| 26 | - | 465, | |
| 27 | - | "me@example.com", | |
| 28 | - | "pw", | |
| 29 | - | true, | |
| 30 | - | Some("Archive"), | |
| 23 | + | NewEmailAccount { | |
| 24 | + | account_name: "Test", | |
| 25 | + | email_address: "me@example.com", | |
| 26 | + | imap_server: "imap.example.com", | |
| 27 | + | imap_port: 993, | |
| 28 | + | smtp_server: "smtp.example.com", | |
| 29 | + | smtp_port: 465, | |
| 30 | + | username: "me@example.com", | |
| 31 | + | password: "pw", | |
| 32 | + | use_tls: true, | |
| 33 | + | archive_folder_name: Some("Archive"), | |
| 34 | + | }, | |
| 31 | 35 | ) | |
| 32 | 36 | .await | |
| 33 | 37 | .expect("create account"); |
| @@ -12,7 +12,9 @@ | |||
| 12 | 12 | ||
| 13 | 13 | use tracing::instrument; | |
| 14 | 14 | ||
| 15 | - | use goingson_core::{EmailAccount, EmailAccountId, EmailAuthType}; | |
| 15 | + | use goingson_core::{ | |
| 16 | + | EmailAccount, EmailAccountId, EmailAuthType, NewEmailAccount, UpdateEmailAccount, | |
| 17 | + | }; | |
| 16 | 18 | ||
| 17 | 19 | use super::{ApiError, OptionApiError, OptionNotFound, ResultApiError}; | |
| 18 | 20 | use crate::email::{ImapClient, SmtpClient, uses_jmap}; | |
| @@ -297,16 +299,18 @@ | |||
| 297 | 299 | .email_accounts | |
| 298 | 300 | .create( | |
| 299 | 301 | DESKTOP_USER_ID, | |
| 300 | - | &input.account_name, | |
| 301 | - | &input.email_address, | |
| 302 | - | &input.imap_server, | |
| 303 | - | input.imap_port, | |
| 304 | - | &input.smtp_server, | |
| 305 | - | input.smtp_port, | |
| 306 | - | &input.username, | |
| 307 | - | "", | |
| 308 | - | input.use_tls, | |
| 309 | - | input.archive_folder_name.as_deref(), | |
| 302 | + | NewEmailAccount { | |
| 303 | + | account_name: &input.account_name, | |
| 304 | + | email_address: &input.email_address, | |
| 305 | + | imap_server: &input.imap_server, | |
| 306 | + | imap_port: input.imap_port, | |
| 307 | + | smtp_server: &input.smtp_server, | |
| 308 | + | smtp_port: input.smtp_port, | |
| 309 | + | username: &input.username, | |
| 310 | + | password: "", | |
| 311 | + | use_tls: input.use_tls, | |
| 312 | + | archive_folder_name: input.archive_folder_name.as_deref(), | |
| 313 | + | }, | |
| 310 | 314 | ) | |
| 311 | 315 | .await?; | |
| 312 | 316 | ||
| @@ -367,16 +371,18 @@ | |||
| 367 | 371 | .update( | |
| 368 | 372 | id, | |
| 369 | 373 | DESKTOP_USER_ID, | |
| 370 | - | &input.account_name, | |
| 371 | - | &input.email_address, | |
| 372 | - | &input.imap_server, | |
| 373 | - | input.imap_port, | |
| 374 | - | &input.smtp_server, | |
| 375 | - | input.smtp_port, | |
| 376 | - | &input.username, | |
| 377 | - | db_password, | |
| 378 | - | input.use_tls, | |
| 379 | - | input.archive_folder_name.as_deref(), | |
| 374 | + | UpdateEmailAccount { | |
| 375 | + | account_name: &input.account_name, | |
| 376 | + | email_address: &input.email_address, | |
| 377 | + | imap_server: &input.imap_server, | |
| 378 | + | imap_port: input.imap_port, | |
| 379 | + | smtp_server: &input.smtp_server, | |
| 380 | + | smtp_port: input.smtp_port, | |
| 381 | + | username: &input.username, | |
| 382 | + | password: db_password, | |
| 383 | + | use_tls: input.use_tls, | |
| 384 | + | archive_folder_name: input.archive_folder_name.as_deref(), | |
| 385 | + | }, | |
| 380 | 386 | ) | |
| 381 | 387 | .await? | |
| 382 | 388 | .or_not_found("emailAccount", id) |
| @@ -165,6 +165,45 @@ | |||
| 165 | 165 | pub notify_new_emails: bool, | |
| 166 | 166 | } | |
| 167 | 167 | ||
| 168 | + | /// Fields needed to create a password-auth IMAP/SMTP account. | |
| 169 | + | /// | |
| 170 | + | /// Named rather than positional: the flat form was eleven arguments with two | |
| 171 | + | /// adjacent `&str` server pairs and an adjacent username/password pair, all | |
| 172 | + | /// silently swappable at a call site. | |
| 173 | + | #[derive(Debug, Clone)] | |
| 174 | + | pub struct NewEmailAccount<'a> { | |
| 175 | + | pub account_name: &'a str, | |
| 176 | + | pub email_address: &'a str, | |
| 177 | + | pub imap_server: &'a str, | |
| 178 | + | pub imap_port: i32, | |
| 179 | + | pub smtp_server: &'a str, | |
| 180 | + | pub smtp_port: i32, | |
| 181 | + | pub username: &'a str, | |
| 182 | + | /// Password to write to the DB column. Callers store the real secret in the | |
| 183 | + | /// OS keychain and pass `""` here. | |
| 184 | + | pub password: &'a str, | |
| 185 | + | pub use_tls: bool, | |
| 186 | + | pub archive_folder_name: Option<&'a str>, | |
| 187 | + | } | |
| 188 | + | ||
| 189 | + | /// Fields needed to update a password-auth IMAP/SMTP account. | |
| 190 | + | /// | |
| 191 | + | /// `password` is tri-state: `None` leaves the column untouched, `Some("")` | |
| 192 | + | /// clears any stale plaintext, `Some(pwd)` writes it. | |
| 193 | + | #[derive(Debug, Clone)] | |
| 194 | + | pub struct UpdateEmailAccount<'a> { | |
| 195 | + | pub account_name: &'a str, | |
| 196 | + | pub email_address: &'a str, | |
| 197 | + | pub imap_server: &'a str, | |
| 198 | + | pub imap_port: i32, | |
| 199 | + | pub smtp_server: &'a str, | |
| 200 | + | pub smtp_port: i32, | |
| 201 | + | pub username: &'a str, | |
| 202 | + | pub password: Option<&'a str>, | |
| 203 | + | pub use_tls: bool, | |
| 204 | + | pub archive_folder_name: Option<&'a str>, | |
| 205 | + | } | |
| 206 | + | ||
| 168 | 207 | /// Per-folder IMAP sync state for incremental UID-based fetching. | |
| 169 | 208 | #[derive(Debug, Clone)] | |
| 170 | 209 | pub struct FolderSyncState { |
| @@ -1,7 +1,7 @@ | |||
| 1 | 1 | use super::{ | |
| 2 | 2 | Attachment, AttachmentId, DateTime, Email, EmailAccount, EmailAccountId, EmailAuthType, | |
| 3 | - | EmailId, EmailThread, FolderSyncState, HashSet, NewAttachment, NewEmail, NewEmailWithTracking, | |
| 4 | - | ProjectId, Result, TaskId, UserId, Utc, async_trait, | |
| 3 | + | EmailId, EmailThread, FolderSyncState, HashSet, NewAttachment, NewEmail, NewEmailAccount, | |
| 4 | + | NewEmailWithTracking, ProjectId, Result, TaskId, UpdateEmailAccount, UserId, Utc, async_trait, | |
| 5 | 5 | }; | |
| 6 | 6 | ||
| 7 | 7 | /// Repository for email message operations. | |
| @@ -213,20 +213,7 @@ | |||
| 213 | 213 | async fn get_by_id(&self, id: EmailAccountId, user_id: UserId) -> Result<Option<EmailAccount>>; | |
| 214 | 214 | ||
| 215 | 215 | /// Creates a new email account configuration (password-based IMAP/SMTP). | |
| 216 | - | async fn create( | |
| 217 | - | &self, | |
| 218 | - | user_id: UserId, | |
| 219 | - | account_name: &str, | |
| 220 | - | email_address: &str, | |
| 221 | - | imap_server: &str, | |
| 222 | - | imap_port: i32, | |
| 223 | - | smtp_server: &str, | |
| 224 | - | smtp_port: i32, | |
| 225 | - | username: &str, | |
| 226 | - | password: &str, | |
| 227 | - | use_tls: bool, | |
| 228 | - | archive_folder_name: Option<&str>, | |
| 229 | - | ) -> Result<EmailAccount>; | |
| 216 | + | async fn create(&self, user_id: UserId, account: NewEmailAccount<'_>) -> Result<EmailAccount>; | |
| 230 | 217 | ||
| 231 | 218 | /// Creates a new OAuth2 email account (Fastmail JMAP). | |
| 232 | 219 | async fn create_oauth( | |
| @@ -262,16 +249,7 @@ | |||
| 262 | 249 | &self, | |
| 263 | 250 | id: EmailAccountId, | |
| 264 | 251 | user_id: UserId, | |
| 265 | - | account_name: &str, | |
| 266 | - | email_address: &str, | |
| 267 | - | imap_server: &str, | |
| 268 | - | imap_port: i32, | |
| 269 | - | smtp_server: &str, | |
| 270 | - | smtp_port: i32, | |
| 271 | - | username: &str, | |
| 272 | - | password: Option<&str>, | |
| 273 | - | use_tls: bool, | |
| 274 | - | archive_folder_name: Option<&str>, | |
| 252 | + | account: UpdateEmailAccount<'_>, | |
| 275 | 253 | ) -> Result<Option<EmailAccount>>; | |
| 276 | 254 | ||
| 277 | 255 | /// Updates OAuth2 tokens for an account. |
| @@ -21,10 +21,10 @@ | |||
| 21 | 21 | use crate::error::CoreError; | |
| 22 | 22 | use crate::models::{ | |
| 23 | 23 | Annotation, Attachment, Email, EmailAccount, EmailAuthType, EmailThread, Event, | |
| 24 | - | FolderSyncState, NewAttachment, NewEmail, NewEmailWithTracking, NewEvent, NewProblem, | |
| 25 | - | NewProject, NewSavedView, NewTask, PositiveMinutes, Problem, ProblemStatus, Project, SavedView, | |
| 26 | - | StatusToken, Subtask, Task, TaskFilterQuery, TimeSession, TimeTrackingSummary, TokenState, | |
| 27 | - | UpdateTask, User, | |
| 24 | + | FolderSyncState, NewAttachment, NewEmail, NewEmailAccount, NewEmailWithTracking, NewEvent, | |
| 25 | + | NewProblem, NewProject, NewSavedView, NewTask, PositiveMinutes, Problem, ProblemStatus, | |
| 26 | + | Project, SavedView, StatusToken, Subtask, Task, TaskFilterQuery, TimeSession, | |
| 27 | + | TimeTrackingSummary, TokenState, UpdateEmailAccount, UpdateTask, User, | |
| 28 | 28 | }; | |
| 29 | 29 | ||
| 30 | 30 | /// Convenience type alias for repository operation results. |
| @@ -7,7 +7,7 @@ | |||
| 7 | 7 | use chrono::{DateTime, Utc}; | |
| 8 | 8 | use goingson_core::{ | |
| 9 | 9 | CoreError, EmailAccount, EmailAccountId, EmailAccountRepository, EmailAuthType, | |
| 10 | - | FolderSyncState, Result, UserId, | |
| 10 | + | FolderSyncState, NewEmailAccount, Result, UpdateEmailAccount, UserId, | |
| 11 | 11 | }; | |
| 12 | 12 | use sqlx::SqlitePool; | |
| 13 | 13 | ||
| @@ -135,20 +135,19 @@ | |||
| 135 | 135 | } | |
| 136 | 136 | ||
| 137 | 137 | #[tracing::instrument(skip_all)] | |
| 138 | - | async fn create( | |
| 139 | - | &self, | |
| 140 | - | user_id: UserId, | |
| 141 | - | account_name: &str, | |
| 142 | - | email_address: &str, | |
| 143 | - | imap_server: &str, | |
| 144 | - | imap_port: i32, | |
| 145 | - | smtp_server: &str, | |
| 146 | - | smtp_port: i32, | |
| 147 | - | username: &str, | |
| 148 | - | password: &str, | |
| 149 | - | use_tls: bool, | |
| 150 | - | archive_folder_name: Option<&str>, | |
| 151 | - | ) -> Result<EmailAccount> { | |
| 138 | + | async fn create(&self, user_id: UserId, account: NewEmailAccount<'_>) -> Result<EmailAccount> { | |
| 139 | + | let NewEmailAccount { | |
| 140 | + | account_name, | |
| 141 | + | email_address, | |
| 142 | + | imap_server, | |
| 143 | + | imap_port, | |
| 144 | + | smtp_server, | |
| 145 | + | smtp_port, | |
| 146 | + | username, | |
| 147 | + | password, | |
| 148 | + | use_tls, | |
| 149 | + | archive_folder_name, | |
| 150 | + | } = account; | |
| 152 | 151 | let id = EmailAccountId::new(); | |
| 153 | 152 | let now = format_datetime_now(); | |
| 154 | 153 | sqlx::query(r" | |
| @@ -251,17 +250,20 @@ | |||
| 251 | 250 | &self, | |
| 252 | 251 | id: EmailAccountId, | |
| 253 | 252 | user_id: UserId, | |
| 254 | - | account_name: &str, | |
| 255 | - | email_address: &str, | |
| 256 | - | imap_server: &str, | |
| 257 | - | imap_port: i32, | |
| 258 | - | smtp_server: &str, | |
| 259 | - | smtp_port: i32, | |
| 260 | - | username: &str, | |
| 261 | - | password: Option<&str>, | |
| 262 | - | use_tls: bool, | |
| 263 | - | archive_folder_name: Option<&str>, | |
| 253 | + | account: UpdateEmailAccount<'_>, | |
| 264 | 254 | ) -> Result<Option<EmailAccount>> { | |
| 255 | + | let UpdateEmailAccount { | |
| 256 | + | account_name, | |
| 257 | + | email_address, | |
| 258 | + | imap_server, | |
| 259 | + | imap_port, | |
| 260 | + | smtp_server, | |
| 261 | + | smtp_port, | |
| 262 | + | username, | |
| 263 | + | password, | |
| 264 | + | use_tls, | |
| 265 | + | archive_folder_name, | |
| 266 | + | } = account; | |
| 265 | 267 | let result = if let Some(pwd) = password { | |
| 266 | 268 | sqlx::query(r" | |
| 267 | 269 | UPDATE email_accounts SET account_name = ?, email_address = ?, imap_server = ?, imap_port = ?, |