feat(contacts): add API to address-books, contacts, groups

* full gateway to /carddav
 * add system readonly address-book issued from OxiCloud users
This commit is contained in:
Edouard Vanbelle
2026-05-09 19:18:33 +02:00
parent a6eb81a13a
commit 89ec0d2c24
6 changed files with 1159 additions and 12 deletions
File diff suppressed because it is too large Load Diff
+1
View File
@@ -5,6 +5,7 @@ pub mod batch_handler;
pub mod caldav_handler;
pub mod carddav_handler;
pub mod chunked_upload_handler;
pub mod contacts_handler;
pub mod dedup_handler;
pub mod device_auth_handler;
pub mod favorites_handler;
+39
View File
@@ -8,6 +8,9 @@ pub use routes::create_public_api_routes;
use utoipa::OpenApi;
use crate::application::dtos::contact_dto::{
AddressDto, ContactDto, ContactGroupDto, EmailDto, PhoneDto,
};
use crate::application::dtos::favorites_dto::{
BatchFavoritesResult, BatchFavoritesStats, FavoriteItemDto,
};
@@ -41,6 +44,10 @@ use crate::application::ports::chunked_upload_ports::{
use crate::interfaces::api::handlers::chunked_upload_handler::{
CompleteUploadResponse, CreateUploadRequest,
};
use crate::interfaces::api::handlers::contacts_handler::{
AddMemberRequest, AddressBookResponse, CreateAddressBookRequest, CreateContactRequest,
GroupNameRequest, UpdateAddressBookRequest, UpdateContactRequest,
};
use crate::interfaces::api::handlers::dedup_handler::{
DedupUploadResponse, HashCheckResponse, StatsResponse,
};
@@ -147,6 +154,24 @@ use crate::interfaces::api::handlers::file_handler::MoveFilePayload;
handlers::music_handler::remove_share,
handlers::music_handler::get_playlist_shares,
handlers::music_handler::get_audio_metadata,
// Contacts / address-book handlers (free functions)
handlers::contacts_handler::list_address_books,
handlers::contacts_handler::create_address_book,
handlers::contacts_handler::update_address_book,
handlers::contacts_handler::delete_address_book,
handlers::contacts_handler::list_contacts,
handlers::contacts_handler::create_contact,
handlers::contacts_handler::get_contact,
handlers::contacts_handler::update_contact,
handlers::contacts_handler::delete_contact,
handlers::contacts_handler::list_groups,
handlers::contacts_handler::create_group,
handlers::contacts_handler::get_group,
handlers::contacts_handler::update_group,
handlers::contacts_handler::delete_group,
handlers::contacts_handler::list_contacts_in_group,
handlers::contacts_handler::add_contact_to_group,
handlers::contacts_handler::remove_contact_from_group,
// Admin handlers (pub free functions)
handlers::admin_handler::get_dashboard_stats,
handlers::admin_handler::list_users,
@@ -231,6 +256,19 @@ use crate::interfaces::api::handlers::file_handler::MoveFilePayload;
HashCheckResponse,
DedupUploadResponse,
StatsResponse,
// Contacts / address-book schemas
AddressBookResponse,
CreateAddressBookRequest,
UpdateAddressBookRequest,
ContactDto,
ContactGroupDto,
EmailDto,
PhoneDto,
AddressDto,
CreateContactRequest,
UpdateContactRequest,
GroupNameRequest,
AddMemberRequest,
)
),
tags(
@@ -247,6 +285,7 @@ use crate::interfaces::api::handlers::file_handler::MoveFilePayload;
(name = "dedup", description = "Content deduplication endpoints"),
(name = "batch", description = "Batch operation endpoints"),
(name = "playlists", description = "Music playlist endpoints"),
(name = "contacts", description = "Address books, contacts, and groups endpoints"),
(name = "admin", description = "Admin management endpoints"),
),
info(
+61
View File
@@ -392,6 +392,67 @@ pub fn create_api_routes(app_state: &Arc<AppState>) -> Router<Arc<AppState>> {
tracing::info!("Music routes initialized");
}
// REST browse API for CardDAV contacts, groups, and OxiCloud users.
// Write operations and protocol sync remain on the /carddav endpoint.
if let Some(contact_service) = app_state.contact_use_case.clone() {
use crate::interfaces::api::handlers::contacts_handler::{self, ContactsApiState};
let auth_svc = app_state
.auth_service
.as_ref()
.map(|s| s.auth_application_service.clone());
let contacts_state = ContactsApiState {
contact_service,
auth_service: auth_svc,
};
let contacts_router = Router::new()
.route(
"/",
get(contacts_handler::list_address_books)
.post(contacts_handler::create_address_book),
)
.route(
"/{book_id}",
put(contacts_handler::update_address_book)
.delete(contacts_handler::delete_address_book),
)
.route(
"/{book_id}/contacts",
get(contacts_handler::list_contacts).post(contacts_handler::create_contact),
)
.route(
"/{book_id}/contacts/{contact_id}",
get(contacts_handler::get_contact)
.put(contacts_handler::update_contact)
.delete(contacts_handler::delete_contact),
)
.route(
"/{book_id}/groups",
get(contacts_handler::list_groups).post(contacts_handler::create_group),
)
.route(
"/{book_id}/groups/{group_id}",
get(contacts_handler::get_group)
.put(contacts_handler::update_group)
.delete(contacts_handler::delete_group),
)
.route(
"/{book_id}/groups/{group_id}/contacts",
get(contacts_handler::list_contacts_in_group)
.post(contacts_handler::add_contact_to_group),
)
.route(
"/{book_id}/groups/{group_id}/contacts/{contact_id}",
delete(contacts_handler::remove_contact_from_group),
)
.with_state(contacts_state);
router = router.nest("/address-books", contacts_router);
tracing::info!("Contacts REST API routes initialized");
}
// NOTE: WebDAV routes are mounted at top-level (/webdav) in main.rs
// for client compatibility, NOT under /api.