Skip to main content

koprogo_api/infrastructure/web/
routes.rs

1use crate::infrastructure::configure_swagger_ui;
2use crate::infrastructure::web::handlers::*;
3use actix_web::web;
4
5pub fn configure_routes(cfg: &mut web::ServiceConfig) {
6    // OpenAPI Swagger UI (accessible at /swagger-ui/)
7    cfg.service(configure_swagger_ui());
8
9    cfg.service(metrics_endpoint);
10
11    // MCP (Model Context Protocol) — Issue #252, #262, #263
12    // SSE transport for Claude/AI integration (JSON-RPC 2.0 over SSE)
13    // Note: /mcp/info is public (no auth); /mcp/sse, /mcp/messages, /mcp/system-prompt, /mcp/legal-index require JWT
14    cfg.service(mcp_info_endpoint); // GET /mcp/info — no auth required
15    cfg.service(mcp_sse_endpoint); // GET /mcp/sse — JWT required
16    cfg.service(mcp_messages_endpoint); // POST /mcp/messages — JWT required
17    cfg.service(mcp_system_prompt_endpoint); // GET /mcp/system-prompt — JWT required (Issue #263)
18    cfg.service(mcp_legal_index_endpoint); // GET /mcp/legal-index — JWT required (Issue #262)
19
20    cfg.service(
21        web::scope("/api/v1")
22            .service(health_check)
23            // Public Endpoints (no authentication required - Issue #92, #277)
24            .service(get_public_syndic_info)
25            // Legal Reference (Issue #277)
26            .service(list_legal_rules)
27            .service(get_legal_rule)
28            .service(get_ag_sequence)
29            .service(get_majority_for)
30            // Authentication
31            .service(login)
32            .service(register)
33            .service(refresh_token)
34            .service(switch_role)
35            .service(logout)
36            .service(get_current_user)
37            // ACPs (Story 1.1 — Association des Copropriétaires, ADR-0010)
38            .service(create_acp)
39            // AVANT `get_acp` : `/acps/with-metrics` serait sinon capté par
40            // `/acps/{id}`, qui tenterait de lire « with-metrics » comme un
41            // UUID et rendrait un 400 au lieu de la liste.
42            .service(list_acps_with_metrics)
43            .service(list_acps)
44            .service(get_acp)
45            .service(update_acp)
46            .service(archive_acp)
47            // Registre de modules par ACP (Story 5.1 #585, ADR-0015).
48            // Trois segments : aucun risque de capture par `/acps/{id}`.
49            .service(list_acp_modules)
50            .service(enable_acp_module)
51            .service(disable_acp_module)
52            // Portfolios (Story 2.1 — Slice 2 Refonte UX multi-rôle, ADR-0011)
53            .service(create_portfolio)
54            .service(list_portfolios)
55            .service(get_portfolio)
56            .service(update_portfolio)
57            .service(delete_portfolio)
58            .service(add_portfolio_building)
59            .service(list_portfolio_buildings)
60            .service(remove_portfolio_building)
61            .service(share_portfolio)
62            .service(list_portfolio_shares)
63            .service(unshare_portfolio)
64            // Buildings
65            .service(create_building)
66            .service(list_buildings)
67            .service(get_building)
68            .service(update_building)
69            .service(delete_building)
70            .service(export_annual_report_pdf) // PDF Export (Issue #47)
71            // Units
72            .service(create_unit)
73            .service(list_units)
74            .service(get_unit)
75            .service(update_unit)
76            .service(delete_unit)
77            .service(list_units_by_building)
78            .service(assign_owner)
79            // Owners
80            // IMPORTANT: /owners/me MUST be registered before /owners/{id}
81            // because {id} would catch "me" as a path parameter
82            .service(get_my_owner)
83            .service(create_owner)
84            .service(list_owners)
85            .service(get_owner)
86            .service(update_owner)
87            .service(link_owner_to_user)
88            .service(export_owner_statement_pdf) // PDF Export (Issue #47)
89            // Owner Contributions (Revenue)
90            // IMPORTANT: register specific routes BEFORE parameterized {id} routes
91            .service(create_contribution)
92            .service(get_contributions_by_owner)
93            .service(get_outstanding_contributions)
94            .service(get_contribution)
95            .service(record_payment)
96            // Call for Funds (Collective payment requests)
97            .service(create_call_for_funds)
98            // Même piège : `/call-for-funds/overdue` avant `/call-for-funds/{id}`.
99            .service(get_overdue_calls)
100            .service(get_call_for_funds)
101            .service(list_call_for_funds)
102            .service(send_call_for_funds)
103            .service(cancel_call_for_funds)
104            .service(delete_call_for_funds)
105            // Unit-Owner Relationships
106            .service(add_owner_to_unit)
107            .service(remove_owner_from_unit)
108            .service(update_unit_owner)
109            .service(get_unit_owners)
110            .service(get_owner_units)
111            .service(get_unit_ownership_history)
112            .service(get_owner_ownership_history)
113            .service(transfer_ownership)
114            .service(get_total_ownership_percentage)
115            .service(designate_voting_representative)
116            .service(export_ownership_contract_pdf) // PDF Export (Issue #47)
117            // Expenses
118            .service(create_expense)
119            .service(list_expenses)
120            .service(get_expense)
121            .service(list_expenses_by_building)
122            .service(mark_expense_paid)
123            .service(mark_expense_overdue)
124            .service(cancel_expense)
125            .service(reactivate_expense)
126            .service(unpay_expense)
127            // Invoice Workflow (Issue #73)
128            .service(create_invoice_draft)
129            .service(update_invoice_draft)
130            .service(submit_invoice_for_approval)
131            .service(approve_invoice)
132            .service(reject_invoice)
133            .service(get_pending_invoices)
134            .service(get_invoice)
135            .service(export_work_quote_pdf) // PDF Export (Issue #47)
136            // Charge Distribution (Issue #73)
137            .service(calculate_and_save_distribution)
138            .service(get_distribution_by_expense)
139            .service(get_distributions_by_owner)
140            .service(get_total_due_by_owner)
141            // Meetings
142            .service(create_meeting)
143            .service(list_meetings)
144            .service(get_meeting)
145            .service(list_meetings_by_building)
146            .service(update_meeting)
147            .service(add_agenda_item)
148            .service(complete_meeting)
149            .service(get_meeting_completion_checklist) // GET /meetings/{id}/completion-checklist (Track H Story H3)
150            .service(cancel_meeting)
151            .service(reschedule_meeting)
152            .service(delete_meeting)
153            .service(attach_minutes)
154            .service(validate_meeting_quorum) // POST /meetings/{id}/validate-quorum (Art. 3.87 §5 CC — #271/#272)
155            .service(export_meeting_minutes_pdf)
156            // Convocations (Automatic AG Invitations - Issue #88 - Phase 2)
157            // Specific routes MUST come before parameterized /convocations/{id}
158            .service(schedule_second_convocation) // POST /convocations/second (must come before /convocations/{id})
159            .service(create_convocation)
160            .service(get_convocation_by_meeting)
161            .service(list_building_convocations)
162            .service(list_organization_convocations)
163            .service(list_convocation_recipients)
164            .service(list_eligible_convocation_recipients) // GET /buildings/{id}/eligible-convocation-recipients (#780 verrou 1)
165            .service(get_convocation_tracking_summary)
166            .service(mark_recipient_email_opened)
167            .service(update_recipient_attendance)
168            .service(set_recipient_proxy)
169            .service(send_convocation_reminders)
170            .service(schedule_convocation)
171            .service(send_convocation)
172            .service(cancel_convocation)
173            .service(delete_convocation)
174            .service(get_convocation)
175            // Resolutions & Voting (Issue #46 - Phase 2)
176            .service(create_resolution)
177            .service(get_resolution)
178            .service(list_meeting_resolutions)
179            .service(delete_resolution)
180            .service(cast_vote)
181            .service(list_resolution_votes)
182            .service(change_vote)
183            .service(close_voting)
184            .service(get_meeting_vote_summary)
185            // Polls (Board Decision Polls - Issue #51 - Phase 2)
186            .service(create_poll)
187            .service(get_poll)
188            .service(update_poll)
189            .service(list_polls)
190            .service(find_active_polls)
191            .service(publish_poll)
192            .service(close_poll)
193            .service(cancel_poll)
194            .service(delete_poll)
195            .service(cast_poll_vote)
196            .service(get_poll_results)
197            .service(get_poll_building_statistics)
198            // Tickets (Maintenance Requests - Issue #85 - Phase 2)
199            // Specific routes MUST come before parameterized /tickets/{id}
200            .service(create_ticket)
201            .service(list_assignable_users) // /tickets/assignable-users BEFORE /tickets/{id}
202            .service(list_my_tickets)
203            .service(list_assigned_tickets)
204            .service(get_ticket_statistics_org) // /tickets/statistics (org-level)
205            .service(get_overdue_tickets_org) // /tickets/overdue (org-level)
206            .service(list_tickets_by_status)
207            .service(get_ticket_statistics) // /buildings/{id}/tickets/statistics
208            .service(get_overdue_tickets) // /buildings/{id}/tickets/overdue
209            .service(list_building_tickets)
210            .service(list_organization_tickets)
211            .service(get_ticket)
212            .service(update_ticket_fields)
213            .service(delete_ticket)
214            .service(assign_ticket)
215            .service(start_work)
216            .service(send_work_order)
217            .service(resolve_ticket)
218            .service(close_ticket)
219            .service(cancel_ticket)
220            .service(reopen_ticket)
221            // Notifications (Multi-Channel System - Issue #86 - Phase 2)
222            // Specific routes MUST come before parameterized /notifications/{id}
223            .service(create_notification)
224            .service(list_my_notifications)
225            .service(list_unread_notifications)
226            .service(mark_all_notifications_read)
227            .service(get_notification_stats)
228            .service(get_notification)
229            .service(mark_notification_read)
230            .service(delete_notification)
231            // Notification Preferences
232            .service(get_user_preferences)
233            .service(get_preference)
234            .service(update_preference)
235            // Payments (Issue #84 - Phase 2)
236            // Specific routes MUST come before parameterized /payments/{id}
237            .service(create_payment)
238            .service(get_payment_by_stripe_intent)
239            .service(list_payments_by_status)
240            .service(list_pending_payments)
241            .service(list_failed_payments)
242            .service(list_owner_payments)
243            .service(list_building_payments)
244            .service(list_expense_payments)
245            .service(list_organization_payments)
246            .service(get_payment)
247            .service(mark_payment_processing)
248            .service(mark_payment_requires_action)
249            .service(mark_payment_succeeded)
250            .service(mark_payment_failed)
251            .service(mark_payment_cancelled)
252            .service(refund_payment)
253            .service(delete_payment)
254            .service(get_owner_payment_stats)
255            .service(get_building_payment_stats)
256            .service(get_expense_total_paid)
257            .service(get_owner_total_paid)
258            .service(get_building_total_paid)
259            // Payment Methods (Issue #84 - Phase 2)
260            .service(create_payment_method)
261            .service(get_payment_method)
262            .service(get_payment_method_by_stripe_id)
263            .service(list_owner_payment_methods)
264            .service(list_active_owner_payment_methods)
265            .service(get_default_payment_method)
266            .service(list_organization_payment_methods)
267            .service(list_payment_methods_by_type)
268            .service(update_payment_method)
269            .service(set_payment_method_as_default)
270            .service(deactivate_payment_method)
271            .service(reactivate_payment_method)
272            .service(delete_payment_method)
273            .service(count_active_payment_methods)
274            .service(has_active_payment_methods)
275            // Quotes (Contractor Quotes Module - Issue #91 - Phase 2)
276            .service(create_quote)
277            .service(get_quote)
278            .service(list_building_quotes)
279            .service(list_contractor_quotes)
280            .service(list_quotes_by_status)
281            .service(submit_quote)
282            .service(start_review)
283            .service(accept_quote)
284            .service(reject_quote)
285            .service(withdraw_quote)
286            .service(compare_quotes)
287            .service(update_contractor_rating)
288            .service(delete_quote)
289            .service(count_building_quotes)
290            .service(count_quotes_by_status)
291            // Local Exchanges (SEL - Local Exchange Trading System - Issue #49 - Phase 2)
292            .service(create_exchange)
293            .service(get_exchange)
294            .service(list_building_exchanges)
295            .service(list_available_exchanges)
296            .service(list_owner_exchanges)
297            .service(list_exchanges_by_type)
298            .service(request_exchange)
299            .service(start_exchange)
300            .service(complete_exchange)
301            .service(cancel_exchange)
302            .service(rate_provider)
303            .service(rate_requester)
304            .service(delete_exchange)
305            .service(get_credit_balance)
306            .service(get_leaderboard)
307            .service(get_sel_statistics)
308            .service(get_owner_summary)
309            // Notices (Community Notice Board - Issue #49 - Phase 2)
310            .service(create_notice)
311            .service(get_notice)
312            .service(list_building_notices)
313            .service(list_published_notices)
314            .service(list_pinned_notices)
315            .service(list_notices_by_type)
316            .service(list_notices_by_category)
317            .service(list_notices_by_status)
318            .service(list_author_notices)
319            .service(update_notice)
320            .service(publish_notice)
321            .service(archive_notice)
322            .service(pin_notice)
323            .service(unpin_notice)
324            .service(set_expiration)
325            .service(delete_notice)
326            .service(get_notice_statistics)
327            // Skills (Skills Directory - Issue #49 - Phase 3)
328            .service(create_skill)
329            .service(get_skill)
330            .service(list_building_skills)
331            .service(list_available_skills)
332            .service(list_free_skills)
333            .service(list_professional_skills)
334            .service(list_skills_by_category)
335            .service(list_skills_by_expertise)
336            .service(list_owner_skills)
337            .service(update_skill)
338            .service(mark_skill_available)
339            .service(mark_skill_unavailable)
340            .service(delete_skill)
341            .service(get_skill_statistics)
342            // Shared Objects (Object Sharing Library - Issue #49 - Phase 4)
343            // Specific routes MUST come before parameterized /shared-objects/{id}
344            .service(create_shared_object)
345            .service(list_my_borrowed_objects)
346            .service(list_building_objects)
347            .service(list_available_objects)
348            .service(list_borrowed_objects)
349            .service(list_overdue_objects)
350            .service(list_free_objects)
351            .service(list_objects_by_category)
352            .service(list_owner_objects)
353            .service(get_shared_object)
354            .service(update_shared_object)
355            .service(mark_object_available)
356            .service(mark_object_unavailable)
357            .service(borrow_object)
358            .service(return_object)
359            .service(delete_shared_object)
360            .service(get_object_statistics)
361            // Resource Bookings (Resource Booking Calendar - Issue #49 - Phase 5)
362            // Specific routes MUST come before parameterized /resource-bookings/{id}
363            .service(create_booking)
364            .service(list_my_bookings)
365            .service(list_my_bookings_by_status)
366            .service(list_active_bookings)
367            .service(check_conflicts)
368            .service(list_building_bookings)
369            .service(list_by_resource_type)
370            .service(list_by_resource)
371            .service(list_building_bookings_by_status)
372            .service(list_upcoming_bookings)
373            .service(list_past_bookings)
374            .service(get_booking_statistics)
375            .service(get_booking)
376            .service(update_booking)
377            .service(cancel_booking)
378            .service(complete_booking)
379            .service(mark_no_show)
380            .service(confirm_booking)
381            .service(delete_booking)
382            // Gamification & Achievements (Issue #49 - Phase 6)
383            // Achievements
384            .service(create_achievement)
385            .service(get_achievement)
386            .service(list_achievements)
387            .service(list_achievements_by_category)
388            .service(list_visible_achievements)
389            .service(update_achievement)
390            .service(delete_achievement)
391            // User Achievements
392            .service(award_achievement)
393            .service(get_user_achievements)
394            .service(get_recent_achievements)
395            // Challenges
396            .service(create_challenge)
397            .service(get_challenge)
398            .service(list_challenges)
399            .service(list_challenges_by_status)
400            .service(list_building_challenges)
401            .service(list_active_challenges)
402            .service(update_challenge)
403            .service(activate_challenge)
404            .service(complete_challenge)
405            .service(cancel_challenge)
406            .service(delete_challenge)
407            // Challenge Progress
408            .service(get_challenge_progress)
409            .service(list_challenge_progress)
410            .service(list_user_active_challenges)
411            .service(increment_progress)
412            // Gamification Stats
413            .service(get_gamification_user_stats)
414            .service(get_gamification_leaderboard)
415            // Board Members
416            .service(elect_board_member)
417            .service(get_my_mandates) // Must be before get_board_member to avoid UUID parsing
418            .service(get_board_dashboard) // Must be before get_board_member to avoid UUID parsing
419            .service(get_board_member)
420            .service(list_active_board_members)
421            .service(list_all_board_members)
422            .service(renew_mandate)
423            .service(remove_board_member)
424            .service(get_board_stats)
425            // Board Decisions
426            .service(create_decision)
427            .service(get_decision)
428            .service(list_decisions_by_building)
429            .service(list_decisions_by_status)
430            .service(list_overdue_decisions)
431            .service(update_decision_status)
432            .service(add_notes)
433            .service(complete_decision)
434            .service(get_decision_stats)
435            // CdC — alertes + élection (Story 4.7 / #582)
436            .service(create_cdc_alert)
437            .service(list_cdc_alerts_for_meeting)
438            .service(elect_cdc_members)
439            // Documents
440            .service(upload_document)
441            .service(list_documents)
442            .service(get_document)
443            .service(download_document)
444            .service(list_documents_by_building)
445            .service(list_documents_by_meeting)
446            .service(list_documents_by_expense)
447            .service(link_document_to_meeting)
448            .service(link_document_to_expense)
449            .service(delete_document)
450            // Energy Buying Groups (Achats Groupés d'Énergie - GDPR compliant)
451            .service(create_campaign)
452            .service(list_campaigns)
453            .service(get_campaign)
454            .service(get_campaign_stats)
455            .service(update_campaign_status)
456            .service(delete_campaign)
457            .service(add_offer)
458            .service(list_offers)
459            .service(select_offer)
460            .service(finalize_campaign)
461            .service(upload_bill)
462            .service(get_my_uploads)
463            .service(get_upload)
464            .service(decrypt_consumption)
465            .service(verify_upload)
466            .service(delete_upload)
467            .service(withdraw_consent)
468            .service(get_campaign_uploads)
469            // Individual Members (Issue #280 - Non-copropriétaire energy group members)
470            .service(join_campaign_as_individual) // POST /energy-campaigns/{id}/join-as-individual
471            .service(grant_consent) // POST /energy-campaigns/{campaign_id}/members/{member_id}/consent
472            .service(update_consumption) // PUT /energy-campaigns/{campaign_id}/members/{member_id}/consumption
473            .service(withdraw_from_campaign) // DELETE /energy-campaigns/{campaign_id}/members/{member_id}/withdraw
474            // Marketplace (Issue #276 - Service provider marketplace)
475            .service(search_service_providers) // GET /marketplace/providers
476            .service(get_provider_by_slug) // GET /marketplace/providers/{slug}
477            .service(create_service_provider) // POST /service-providers
478            .service(get_contract_evaluations_annual) // GET /buildings/{id}/reports/contract-evaluations/annual
479            // États Datés (Belgian legal requirement for property sales)
480            // Specific routes MUST come before parameterized /etats-dates/{id}
481            .service(create_etat_date)
482            .service(list_etats_dates)
483            .service(list_overdue)
484            .service(list_expired)
485            .service(get_stats)
486            .service(get_by_reference_number)
487            .service(issue_notary_link) // POST /etats-dates/{id}/notary-link (#845)
488            .service(renew_notary_link) // PUT /etats-dates/{id}/notary-link/renew (#845)
489            .service(revoke_notary_link) // DELETE /etats-dates/{id}/notary-link (#845)
490            .service(list_etats_dates_by_unit)
491            .service(list_etats_dates_by_building)
492            .service(get_etat_date)
493            .service(mark_in_progress)
494            .service(mark_generated)
495            .service(mark_delivered)
496            .service(update_financial_data)
497            .service(update_additional_data)
498            .service(delete_etat_date)
499            // Budgets (Annual budget management)
500            // Specific routes MUST come before parameterized /budgets/{id}
501            .service(create_budget)
502            .service(list_budgets)
503            .service(get_budget_stats)
504            .service(list_budgets_by_fiscal_year)
505            .service(list_budgets_by_status)
506            .service(get_budget_by_building_and_fiscal_year)
507            .service(get_active_budget)
508            .service(list_budgets_by_building)
509            .service(get_budget)
510            .service(get_budget_variance)
511            .service(update_budget)
512            .service(submit_budget)
513            .service(approve_budget)
514            .service(reject_budget)
515            .service(archive_budget)
516            .service(delete_budget)
517            // Accounts (Belgian PCMN)
518            .service(create_account)
519            .service(list_accounts)
520            // `count_accounts` AVANT `get_account` : `/accounts/count` serait
521            // sinon capté par `/accounts/{id}`, qui tenterait de lire « count »
522            // comme un UUID. La route était donc inatteignable, et rendait un
523            // 400 sur un identifiant mal formé plutôt que le décompte.
524            .service(count_accounts)
525            .service(get_account)
526            .service(get_account_by_code)
527            .service(update_account)
528            .service(delete_account)
529            .service(seed_belgian_pcmn)
530            // PCN (Belgian Chart of Accounts - Legacy)
531            .service(generate_pcn_report)
532            .service(export_pcn_pdf)
533            .service(export_pcn_excel)
534            // Financial Reports (Belgian PCMN)
535            .service(generate_balance_sheet)
536            .service(generate_income_statement)
537            .service(generate_balance_sheet_for_building)
538            .service(generate_income_statement_for_building)
539            // Journal Entries (Manual Accounting Operations - Noalyss-inspired)
540            .service(create_journal_entry)
541            .service(list_journal_entries)
542            .service(get_journal_entry)
543            .service(delete_journal_entry)
544            // Dashboard (Accountant)
545            .service(get_accountant_stats)
546            .service(get_recent_transactions)
547            // Payment Reminders (Issue #83)
548            // IMPORTANT: Specific routes BEFORE parameterized routes to avoid UUID conflicts
549            .service(get_recovery_stats) // Must be before get_reminder (/payment-reminders/stats vs /{id})
550            .service(find_overdue_without_reminders) // Must be before get_reminder
551            .service(bulk_create_reminders) // Must be before get_reminder
552            .service(list_by_organization) // /payment-reminders
553            .service(create_reminder) // POST /payment-reminders
554            .service(get_reminder) // /payment-reminders/{id}
555            .service(delete_reminder) // DELETE /payment-reminders/{id}
556            .service(list_by_expense) // /expenses/{id}/payment-reminders
557            .service(list_by_owner) // /owners/{id}/payment-reminders
558            .service(list_active_by_owner) // /owners/{id}/payment-reminders/active
559            .service(mark_as_sent) // PUT /payment-reminders/{id}/mark-sent
560            .service(mark_as_opened) // PUT /payment-reminders/{id}/mark-opened
561            .service(mark_as_paid) // PUT /payment-reminders/{id}/mark-paid
562            .service(cancel_reminder) // PUT /payment-reminders/{id}/cancel
563            .service(escalate_reminder) // POST /payment-reminders/{id}/escalate
564            .service(add_tracking_number) // PUT /payment-reminders/{id}/tracking-number
565            // Seed (SuperAdmin only)
566            .service(seed_demo_data)
567            .service(clear_demo_data)
568            // Scenario-based seeds for E2E testing (Issue #347)
569            .service(seed_scenario_world)
570            .service(clear_scenario_world)
571            // Stats (SuperAdmin only)
572            .service(get_dashboard_stats)
573            .service(get_seed_data_stats)
574            // Stats (Syndic/Accountant)
575            .service(get_syndic_stats)
576            .service(get_syndic_urgent_tasks)
577            // Stats (Owner)
578            // AVANT `get_owner_stats` n'est pas requis ici — `/stats/owner` et
579            // `/stats/owner/dues-by-acp` ne se recouvrent pas, la seconde ayant
580            // un segment de plus. Mais `garde_ordre_des_routes` veille.
581            .service(get_owner_dues_by_acp)
582            .service(get_owner_stats)
583            // Organizations (SuperAdmin only)
584            .service(list_organizations)
585            .service(create_organization)
586            .service(update_organization)
587            .service(activate_organization)
588            .service(suspend_organization)
589            .service(delete_organization)
590            // Users (SuperAdmin only)
591            .service(list_users)
592            .service(list_organization_users)
593            .service(create_user)
594            .service(update_user)
595            .service(activate_user)
596            .service(deactivate_user)
597            .service(delete_user)
598            // GDPR (Data Privacy)
599            .service(export_user_data)
600            .service(erase_user_data)
601            .service(can_erase_user)
602            // GDPR Complementary Articles (Issue #90)
603            .service(rectify_user_data) // Article 16: Right to Rectification
604            .service(restrict_user_processing) // Article 18: Right to Restriction
605            .service(set_marketing_preference) // Article 21: Right to Object
606            // GDPR Consent Recording (Issue #315 - Privacy Policy)
607            .service(record_consent)
608            .service(get_consent_status)
609            // GDPR Admin (SuperAdmin only)
610            .service(list_audit_logs)
611            .service(admin_export_user_data)
612            .service(admin_erase_user_data)
613            // GDPR Article 28 - DPA with Sub-Processors (Issue #316)
614            .service(list_processing_activities)
615            .service(list_sub_processors)
616            // GDPR Article 33 - Security Incidents & 72h APD Notification (Issue #317)
617            // IMPORTANT : routes specifiques AVANT les routes parametrees.
618            //
619            // Actix apparie dans l'ordre d'enregistrement. Avec
620            // `/{incident_id}` declare en premier, un GET sur
621            // `/admin/security-incidents/overdue` etait appareille avec
622            // `incident_id = "overdue"`, l'incident n'existait evidemment pas,
623            // et l'endpoint repondait 404 — donc INATTEIGNABLE en production.
624            //
625            // C'est l'endpoint qui liste les violations depassant le delai de
626            // 72 h de notification a l'APD (RGPD Art. 33). Un instrument de
627            // conformite legale, muet depuis son ecriture.
628            //
629            // Detecte par `e2e_security_incidents::test_list_overdue_incidents`,
630            // harnais jamais execute. Meme precaution deja documentee plus haut
631            // pour `/payment-reminders/stats` face a `/payment-reminders/{id}`.
632            .service(create_security_incident)
633            .service(list_overdue_incidents)
634            .service(list_security_incidents)
635            .service(get_security_incident)
636            .service(report_incident_to_apd)
637            // Two-Factor Authentication (2FA TOTP - Issue #78)
638            .configure(two_factor_handlers::configure_two_factor_routes)
639            // API Key Management (Public API v2 - Issues #111, #232)
640            .service(create_api_key)
641            .service(list_api_keys)
642            .service(get_api_key)
643            .service(update_api_key)
644            .service(revoke_api_key)
645            .service(rotate_api_key)
646            // Work Reports (Digital Maintenance Logbook - Issue #134)
647            .service(create_work_report)
648            .service(get_work_report)
649            .service(list_building_work_reports)
650            .service(list_organization_work_reports)
651            .service(list_work_reports_paginated)
652            .service(update_work_report)
653            .service(delete_work_report)
654            .service(get_active_warranties)
655            .service(get_expiring_warranties)
656            .service(add_photo)
657            .service(add_document)
658            // Technical Inspections (Digital Maintenance Logbook - Issue #134)
659            .service(create_technical_inspection)
660            .service(get_technical_inspection)
661            .service(list_building_technical_inspections)
662            .service(list_organization_technical_inspections)
663            .service(list_technical_inspections_paginated)
664            .service(update_technical_inspection)
665            .service(delete_technical_inspection)
666            .service(get_overdue_inspections)
667            .service(get_upcoming_inspections)
668            .service(get_inspections_by_type)
669            .service(add_report)
670            .service(add_inspection_photo)
671            .service(add_certificate)
672            // IoT Smart Meters (Linky/Ores - Issue #133 - IoT Phase 0)
673            .configure(iot_handlers::configure_iot_routes)
674            // IoT Phase 1: MQTT Home Assistant + BOINC Grid Computing
675            // IMPORTANT: specific /iot/grid/consent must be before /iot/grid/tasks
676            .service(start_mqtt_listener) // POST /iot/mqtt/start
677            .service(stop_mqtt_listener) // POST /iot/mqtt/stop
678            .service(mqtt_status) // GET  /iot/mqtt/status
679            .service(update_grid_consent) // POST /iot/grid/consent
680            .service(get_grid_consent) // GET  /iot/grid/consent/{owner_id}
681            .service(submit_grid_task) // POST /iot/grid/tasks
682            .service(cancel_grid_task) // DELETE /iot/grid/tasks/{task_id}
683            .service(get_task_status) // GET  /iot/grid/tasks/{task_id} — LAST (parameterized)
684            // AG Visioconférence (BC15 - Art. 3.87 §1 CC)
685            // Specific routes BEFORE parameterized /ag-sessions/{id}
686            .service(list_ag_sessions) // GET /ag-sessions
687            .service(get_ag_session_platform_stats) // GET /ag-sessions/platform-stats (Issue #274 - SuperAdmin only)
688            .service(create_ag_session) // POST /meetings/{meeting_id}/ag-session
689            .service(get_ag_session_for_meeting) // GET /meetings/{meeting_id}/ag-session
690            .service(get_combined_quorum) // GET /ag-sessions/{id}/quorum
691            .service(start_ag_session) // PUT /ag-sessions/{id}/start
692            .service(end_ag_session) // PUT /ag-sessions/{id}/end
693            .service(cancel_ag_session) // PUT /ag-sessions/{id}/cancel
694            .service(record_remote_join) // POST /ag-sessions/{id}/join
695            .service(delete_ag_session) // DELETE /ag-sessions/{id}
696            .service(get_ag_session) // GET /ag-sessions/{id} — LAST (parameterized)
697            // AGE Agile & Concertation (BC17 - Art. 3.87 §2 CC)
698            // Specific sub-routes BEFORE parameterized /age-requests/{id}
699            .service(create_age_request) // POST /buildings/{building_id}/age-requests
700            .service(list_age_requests) // GET /buildings/{building_id}/age-requests
701            .service(add_cosignatory) // POST /age-requests/{id}/cosignatories
702            .service(remove_cosignatory) // DELETE /age-requests/{id}/cosignatories/{owner_id}
703            .service(open_age_request) // PUT /age-requests/{id}/open
704            .service(submit_age_request) // POST /age-requests/{id}/submit
705            .service(syndic_response) // PUT /age-requests/{id}/syndic-response
706            .service(trigger_auto_convocation) // POST /age-requests/{id}/auto-convocation
707            .service(withdraw_age_request) // POST /age-requests/{id}/withdraw
708            .service(delete_age_request) // DELETE /age-requests/{id}
709            .service(get_age_request) // GET /age-requests/{id} — LAST (parameterized)
710            // Backoffice Prestataires PWA (BC16)
711            // PWA routes sans auth (magic link) BEFORE routes auth
712            .service(get_report_by_token) // GET /contractor/token/{token}
713            .service(submit_report_by_token) // POST /contractor/token/{token}/submit
714            // NEW: Improved magic link endpoints (Issue #275)
715            .service(get_report_by_magic_token) // GET /contractor-reports/magic/{token}
716            .service(submit_report_by_magic_token) // POST /contractor-reports/magic/{token}/submit
717            // Routes authentifiées — spécifiques BEFORE paramétrées
718            .service(generate_magic_link) // POST /contractor-reports/magic-link
719            .service(list_contractor_reports_by_building) // GET /buildings/{building_id}/contractor-reports
720            .service(list_contractor_reports_by_ticket) // GET /tickets/{ticket_id}/contractor-reports
721            .service(create_contractor_report) // POST /contractor-reports
722            .service(submit_contractor_report) // POST /contractor-reports/{id}/submit
723            .service(validate_contractor_report) // PUT /contractor-reports/{id}/validate
724            .service(request_corrections) // PUT /contractor-reports/{id}/request-corrections
725            .service(reject_contractor_report) // PUT /contractor-reports/{id}/reject
726            .service(update_contractor_report) // PUT /contractor-reports/{id}
727            .service(delete_contractor_report) // DELETE /contractor-reports/{id}
728            .service(get_contractor_report) // GET /contractor-reports/{id} — LAST (parameterized)
729            // Generic MagicLinks (Story 3.2 — FR6 INV-13 INV-17)
730            // POST /magic-links       : syndic/superadmin issues a link
731            // GET  /c/{token}         : PUBLIC (no auth) — validate + consume + resolve scope
732            // POST /c/{token}/respond : PUBLIC (no auth) — write action (#835, ContractorReport)
733            .service(issue_magic_link)
734            .service(consume_magic_link)
735            .service(respond_magic_link)
736            // Mandates (Story 3.4 — FR7 INV-14)
737            // POST   /mandates              : syndic/superadmin issues a mandate
738            // GET    /mandates?subject=<u>  : list active mandates for a subject
739            // GET    /mandates/{id}         : mandate details
740            // POST   /mandates/{id}/revoke  : early revocation
741            .service(issue_mandate)
742            .service(list_mandates)
743            .service(revoke_mandate)
744            .service(get_mandate)
745            // Role Delegations (Story 3.5 — FR8 INV-8)
746            // POST   /role-delegations              : delegate role to another user (bounded)
747            // DELETE /role-delegations/{id}         : revoke a delegation
748            // GET    /role-delegations?subject=<u>  : list delegations of a subject
749            .service(create_role_delegation)
750            .service(revoke_role_delegation)
751            .service(list_role_delegations)
752            // Role Assignments — CRUD REST (Story B0bis — gap Story 3.1)
753            // POST   /users/{user_id}/role-assignments              : assign sub-role
754            // GET    /users/{user_id}/role-assignments              : list user's assignments
755            // DELETE /users/{user_id}/role-assignments/{id}         : revoke assignment
756            // GET    /role-assignments?organization_id=&role=       : superadmin filtered list
757            .service(assign_role)
758            .service(list_role_assignments_for_user)
759            .service(revoke_role_assignment)
760            .service(list_role_assignments_admin)
761            // SyndicResponse (Story 3.7 — FR32 INV-23) — append-only.
762            // POST /tickets/{id}/syndic-responses : syndic/superadmin posts a reply
763            // GET  /tickets/{id}/syndic-responses : list responses for a ticket
764            .service(create_syndic_response)
765            .service(list_syndic_responses)
766            // TechnicalSpec (Story 3.8 — FR33) — versionnable + signatures multi-parties.
767            // POST /technical-specs              : create Draft (syndic/superadmin)
768            // POST /technical-specs/{id}/bump    : new version (syndic/superadmin)
769            // POST /technical-specs/{id}/submit  : Draft -> PendingSignatures
770            // POST /technical-specs/{id}/signatures : record a signature
771            // GET  /technical-specs/{id}         : details
772            // GET  /technical-specs?acp_id=      : list for an ACP
773            .service(create_technical_spec)
774            .service(bump_technical_spec)
775            .service(submit_technical_spec)
776            .service(sign_technical_spec)
777            .service(list_technical_specs)
778            .service(get_technical_spec)
779            // ContractorEvaluation (Story 3.9 — FR34 FR35 INV-21 INV-24) — append-only.
780            // POST /contractor-evaluations                       : record an evaluation
781            // GET  /contractor-evaluations/{id}                  : details
782            // GET  /contractors/{contractor_user_id}/evaluations : list for a contractor
783            .service(create_contractor_evaluation)
784            .service(get_contractor_evaluation)
785            .service(list_contractor_evaluations),
786    );
787}