Skip to main content

remem/api/
server.rs

1use axum::{
2    middleware,
3    routing::{get, post},
4    Extension, Router,
5};
6
7use super::auth::{ensure_api_token, require_api_token};
8use super::handlers::{
9    handle_activity_sessions, handle_approve_candidate, handle_archive_memory,
10    handle_blocked_candidates, handle_candidate_detail, handle_capabilities, handle_edit_candidate,
11    handle_event_detail, handle_get_memory, handle_graph, handle_health, handle_list_candidates,
12    handle_list_events, handle_list_memories, handle_list_observations,
13    handle_list_session_activity, handle_list_sessions, handle_list_tasks, handle_list_workstreams,
14    handle_memory_detail, handle_observation_detail, handle_project_session_activity,
15    handle_reject_candidate, handle_restore_memory, handle_safe_approve_candidate,
16    handle_safe_edit_candidate, handle_safe_reject_candidate, handle_save_memory, handle_search,
17    handle_session_activity_detail, handle_session_activity_stats, handle_session_detail,
18    handle_session_intent_apply, handle_session_intent_preview, handle_stats, handle_status,
19    handle_task_detail, handle_user_recall, handle_workstream_detail,
20};
21use super::types::{DbState, StatusCache};
22
23pub fn build_router(_port: u16) -> Router<DbState> {
24    Router::new()
25        .route("/api/v1/health", get(handle_health))
26        .route("/api/v1/capabilities", get(handle_capabilities))
27        .route("/api/v1/search", get(handle_search))
28        .route("/api/v1/memory", get(handle_get_memory))
29        .route(
30            "/api/v1/memories",
31            get(handle_list_memories).post(handle_save_memory),
32        )
33        .route("/api/v1/user/recall", post(handle_user_recall))
34        .route("/api/v1/status", get(handle_status))
35        .route("/api/v1/memories/list", get(handle_list_memories))
36        .route("/api/v1/memories/{id}", get(handle_memory_detail))
37        .route("/api/v1/memories/{id}/archive", post(handle_archive_memory))
38        .route("/api/v1/memories/{id}/restore", post(handle_restore_memory))
39        .route("/api/v1/candidates", get(handle_list_candidates))
40        .route("/api/v1/candidates/blocked", get(handle_blocked_candidates))
41        .route("/api/v1/candidates/{id}", get(handle_candidate_detail))
42        .route(
43            "/api/v1/candidates/{id}/approve",
44            post(handle_approve_candidate),
45        )
46        .route(
47            "/api/v1/candidates/{id}/reject",
48            post(handle_reject_candidate),
49        )
50        .route("/api/v1/candidates/{id}/edit", post(handle_edit_candidate))
51        .route(
52            "/api/v1/candidates/{id}/review/approve",
53            post(handle_safe_approve_candidate),
54        )
55        .route(
56            "/api/v1/candidates/{id}/review/reject",
57            post(handle_safe_reject_candidate),
58        )
59        .route(
60            "/api/v1/candidates/{id}/review/edit",
61            post(handle_safe_edit_candidate),
62        )
63        .route("/api/v1/observations", get(handle_list_observations))
64        .route("/api/v1/observations/{id}", get(handle_observation_detail))
65        .route(
66            "/api/v1/session-intent/preview",
67            post(handle_session_intent_preview),
68        )
69        .route(
70            "/api/v1/session-intent/apply",
71            post(handle_session_intent_apply),
72        )
73        .route("/api/v1/sessions", get(handle_list_sessions))
74        .route("/api/v1/sessions/{id}", get(handle_session_detail))
75        .route(
76            "/api/v1/session-activity/sessions",
77            get(handle_activity_sessions),
78        )
79        .route(
80            "/api/v1/session-activity/project",
81            post(handle_project_session_activity),
82        )
83        .route(
84            "/api/v1/session-activity",
85            get(handle_list_session_activity),
86        )
87        .route(
88            "/api/v1/session-activity/{id}",
89            get(handle_session_activity_detail),
90        )
91        .route("/api/v1/session-stats", get(handle_session_activity_stats))
92        .route("/api/v1/workstreams", get(handle_list_workstreams))
93        .route("/api/v1/workstreams/{id}", get(handle_workstream_detail))
94        .route("/api/v1/events", get(handle_list_events))
95        .route("/api/v1/events/{id}", get(handle_event_detail))
96        .route("/api/v1/tasks", get(handle_list_tasks))
97        .route("/api/v1/tasks/{id}", get(handle_task_detail))
98        .route("/api/v1/graph", get(handle_graph))
99        .route("/api/v1/stats", get(handle_stats))
100        .route_layer(middleware::from_fn(require_api_token))
101        .layer(Extension(StatusCache::default()))
102}
103
104pub async fn run_api_server(port: u16) -> anyhow::Result<()> {
105    let token_path = ensure_api_token()?;
106    let app = build_router(port).with_state(DbState);
107    let addr = format!("127.0.0.1:{}", port);
108
109    crate::log::info("api", &format!("REST API listening on http://{}", addr));
110    println!(
111        "remem REST API v{} on http://{}",
112        env!("CARGO_PKG_VERSION"),
113        addr
114    );
115    println!(
116        "API token: Authorization: Bearer $(cat {})",
117        token_path.display()
118    );
119
120    let listener = tokio::net::TcpListener::bind(&addr).await?;
121    axum::serve(listener, app).await?;
122    Ok(())
123}