icarus_canister/
endpoints.rs1use crate::state::STATE;
7use candid::{CandidType, Deserialize, Principal};
8use icarus_core::protocol::{IcarusMetadata, ToolMetadata};
9
10pub fn icarus_metadata() -> IcarusMetadata {
12 STATE.with(|s| {
13 let state = s.borrow();
14 if let Some(state) = state.as_ref() {
15 IcarusMetadata {
16 version: state.config.get().version.clone(),
17 canister_id: ic_cdk::api::canister_self(),
18 tools: state
19 .tools
20 .iter()
21 .map(|entry| {
22 let (name, tool_state) = (entry.key().clone(), entry.value());
23 ToolMetadata {
24 name: name.clone(),
25 candid_method: name.clone(), is_query: tool_state.is_query,
27 description: format!("{} tool", name), parameters: vec![], }
30 })
31 .collect(),
32 }
33 } else {
34 IcarusMetadata {
35 version: "1.0.0".to_string(),
36 canister_id: ic_cdk::api::canister_self(),
37 tools: vec![],
38 }
39 }
40 })
41}
42
43#[derive(CandidType, Deserialize)]
45pub struct HttpRequest {
46 pub method: String,
47 pub url: String,
48 pub headers: Vec<(String, String)>,
49 pub body: Vec<u8>,
50}
51
52#[derive(CandidType)]
54pub struct HttpResponse {
55 pub status_code: u16,
56 pub headers: Vec<(String, String)>,
57 pub body: Vec<u8>,
58}
59
60pub fn http_request(_req: HttpRequest) -> HttpResponse {
62 let metadata = icarus_metadata();
63
64 let tools_html: String = metadata
66 .tools
67 .iter()
68 .map(|tool| {
69 format!(
70 r#"<div class="tool">
71 <strong>{}</strong> ({})
72 <br><small>{}</small>
73 </div>"#,
74 tool.name,
75 if tool.is_query { "query" } else { "update" },
76 tool.description
77 )
78 })
79 .collect::<Vec<_>>()
80 .join("\n");
81
82 let html = format!(
83 r#"<!DOCTYPE html>
84<html>
85<head>
86 <title>Icarus Canister</title>
87 <style>
88 body {{
89 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
90 max-width: 800px;
91 margin: 50px auto;
92 padding: 20px;
93 background: #f5f5f5;
94 }}
95 .container {{
96 background: white;
97 padding: 30px;
98 border-radius: 10px;
99 box-shadow: 0 2px 10px rgba(0,0,0,0.1);
100 }}
101 h1 {{
102 color: #333;
103 margin-bottom: 10px;
104 }}
105 .info {{
106 background: #e7f3ff;
107 padding: 15px;
108 border-radius: 5px;
109 border-left: 4px solid #2196F3;
110 margin: 20px 0;
111 }}
112 code {{
113 background: #f0f0f0;
114 padding: 2px 5px;
115 border-radius: 3px;
116 font-family: "Courier New", monospace;
117 }}
118 .tool {{
119 margin: 10px 0;
120 padding: 10px;
121 background: #f9f9f9;
122 border-radius: 5px;
123 }}
124 </style>
125</head>
126<body>
127 <div class="container">
128 <h1>🚀 Icarus Canister</h1>
129 <p>This is a standard ICP canister built with Icarus SDK.</p>
130
131 <div class="info">
132 <strong>Canister ID:</strong> <code>{}</code><br>
133 <strong>Version:</strong> <code>{}</code><br>
134 <strong>Status:</strong> <span style="color: green;">✓ Running</span>
135 </div>
136
137 <h2>Available Tools</h2>
138 {}
139
140 <h2>Connect with Claude Desktop</h2>
141 <p>To use this canister with Claude Desktop, run:</p>
142 <code>icarus bridge start --canister-id {}</code>
143
144 <hr style="margin-top: 40px; border: none; border-top: 1px solid #eee;">
145 <p style="text-align: center; color: #666; font-size: 14px;">
146 Powered by <a href="https://icarus.dev" style="color: #2196F3;">Icarus SDK</a>
147 </p>
148 </div>
149</body>
150</html>"#,
151 ic_cdk::api::canister_self().to_text(),
152 metadata.version,
153 tools_html,
154 ic_cdk::api::canister_self().to_text()
155 );
156
157 HttpResponse {
158 status_code: 200,
159 headers: vec![
160 (
161 "Content-Type".to_string(),
162 "text/html; charset=UTF-8".to_string(),
163 ),
164 ("Cache-Control".to_string(), "no-cache".to_string()),
165 ],
166 body: html.into_bytes(),
167 }
168}
169
170pub fn get_owner() -> Principal {
172 crate::state::get_owner()
173}