mirror of
https://github.com/Xevion/xevion.dev.git
synced 2026-01-31 22:26:33 -06:00
feat: add Iconify-based icon system with search and picker UI
- Replace Font Awesome with Iconify (@iconify/json collections) - Add IconPicker component with search, collection filtering, lazy loading - Create authenticated icon API endpoints (search, collections, individual icons) - Update projects page to render icons via Icon.svelte component - Pre-cache common icon collections (Lucide, Simple Icons, etc.) on startup
This commit is contained in:
+6
-6
@@ -22,7 +22,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Some("Xevion/xevion.dev"),
|
||||
None,
|
||||
10,
|
||||
Some("fa-globe"),
|
||||
Some("lucide:globe"),
|
||||
),
|
||||
(
|
||||
"contest",
|
||||
@@ -32,7 +32,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Some("Xevion/contest"),
|
||||
Some("https://contest.xevion.dev"),
|
||||
9,
|
||||
Some("fa-trophy"),
|
||||
Some("lucide:trophy"),
|
||||
),
|
||||
(
|
||||
"reforge",
|
||||
@@ -42,7 +42,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Some("Xevion/reforge"),
|
||||
None,
|
||||
8,
|
||||
Some("fa-file-code"),
|
||||
Some("lucide:file-code"),
|
||||
),
|
||||
(
|
||||
"algorithms",
|
||||
@@ -52,7 +52,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Some("Xevion/algorithms"),
|
||||
None,
|
||||
5,
|
||||
Some("fa-brain"),
|
||||
Some("lucide:brain"),
|
||||
),
|
||||
(
|
||||
"wordplay",
|
||||
@@ -62,7 +62,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Some("Xevion/wordplay"),
|
||||
Some("https://wordplay.example.com"),
|
||||
7,
|
||||
Some("fa-gamepad"),
|
||||
Some("lucide:gamepad-2"),
|
||||
),
|
||||
(
|
||||
"dotfiles",
|
||||
@@ -72,7 +72,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Some("Xevion/dotfiles"),
|
||||
None,
|
||||
6,
|
||||
Some("fa-terminal"),
|
||||
Some("lucide:terminal"),
|
||||
),
|
||||
];
|
||||
|
||||
|
||||
+53
@@ -378,6 +378,8 @@ fn api_routes() -> Router<Arc<AppState>> {
|
||||
"/tags/recalculate-cooccurrence",
|
||||
axum::routing::post(recalculate_cooccurrence_handler),
|
||||
)
|
||||
// Icon API - proxy to SvelteKit (authentication handled by SvelteKit)
|
||||
.route("/icons/{*path}", axum::routing::get(proxy_icons_handler))
|
||||
.fallback(api_404_and_method_handler)
|
||||
}
|
||||
|
||||
@@ -529,6 +531,57 @@ async fn projects_handler(State(state): State<Arc<AppState>>) -> impl IntoRespon
|
||||
}
|
||||
}
|
||||
|
||||
// Icon API handler - proxy to SvelteKit
|
||||
async fn proxy_icons_handler(
|
||||
State(state): State<Arc<AppState>>,
|
||||
jar: axum_extra::extract::CookieJar,
|
||||
axum::extract::Path(path): axum::extract::Path<String>,
|
||||
req: Request,
|
||||
) -> impl IntoResponse {
|
||||
let full_path = format!("/api/icons/{}", path);
|
||||
let query = req.uri().query().unwrap_or("");
|
||||
|
||||
let bun_url = if state.downstream_url.starts_with('/') || state.downstream_url.starts_with("./")
|
||||
{
|
||||
if query.is_empty() {
|
||||
format!("http://localhost{}", full_path)
|
||||
} else {
|
||||
format!("http://localhost{}?{}", full_path, query)
|
||||
}
|
||||
} else if query.is_empty() {
|
||||
format!("{}{}", state.downstream_url, full_path)
|
||||
} else {
|
||||
format!("{}{}?{}", state.downstream_url, full_path, query)
|
||||
};
|
||||
|
||||
// Build trusted headers with session info
|
||||
let mut forward_headers = HeaderMap::new();
|
||||
|
||||
if let Some(cookie) = jar.get("admin_session") {
|
||||
if let Ok(session_id) = ulid::Ulid::from_string(cookie.value()) {
|
||||
if let Some(session) = state.session_manager.validate_session(session_id) {
|
||||
if let Ok(username_value) = axum::http::HeaderValue::from_str(&session.username) {
|
||||
forward_headers.insert("x-session-user", username_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match proxy_to_bun(&bun_url, state, forward_headers).await {
|
||||
Ok((status, headers, body)) => (status, headers, body).into_response(),
|
||||
Err(err) => {
|
||||
tracing::error!(error = %err, path = %full_path, "Failed to proxy icon request");
|
||||
(
|
||||
StatusCode::BAD_GATEWAY,
|
||||
Json(serde_json::json!({
|
||||
"error": "Failed to fetch icon data"
|
||||
})),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tag API handlers
|
||||
|
||||
async fn list_tags_handler(State(state): State<Arc<AppState>>) -> impl IntoResponse {
|
||||
|
||||
Reference in New Issue
Block a user