Files
openfang/crates/openfang-api/src/webchat.rs
jaberjaber23 5692c96494 Initial commit — OpenFang Agent Operating System
Open-source Agent OS built in Rust.

- 14 crates, 1,767+ tests, zero clippy warnings
- 7 autonomous Hands (Clip, Lead, Collector, Predictor, Researcher, Twitter, Browser)
- 16 security systems (WASM sandbox, Merkle audit trail, taint tracking, Ed25519 signing, SSRF protection, secret zeroization, HMAC-SHA256 mutual auth, and more)
- 30 pre-built agents across 4 performance tiers
- 40 channel adapters (Telegram, Discord, Slack, WhatsApp, Teams, and 35 more)
- 38 built-in tools + MCP client/server + A2A protocol
- 26 LLM providers with intelligent routing and cost tracking
- 60+ bundled skills with FangHub marketplace
- Tauri 2.0 native desktop app
- 140+ REST/WS/SSE API endpoints with Alpine.js dashboard
- OpenAI-compatible /v1/chat/completions endpoint
- One-command install, production-ready
2026-02-26 01:00:27 +03:00

133 lines
4.2 KiB
Rust

//! Embedded WebChat UI served as static HTML.
//!
//! The production dashboard is assembled at compile time from separate
//! HTML/CSS/JS files under `static/` using `include_str!()`. This keeps
//! single-binary deployment while allowing organized source files.
//!
//! Features:
//! - Alpine.js SPA with hash-based routing (10 panels)
//! - Dark/light theme toggle with system preference detection
//! - Responsive layout with collapsible sidebar
//! - Markdown rendering + syntax highlighting (bundled locally)
//! - WebSocket real-time chat with HTTP fallback
//! - Agent management, workflows, memory browser, audit log, and more
use axum::http::header;
use axum::response::IntoResponse;
/// Compile-time ETag based on the crate version.
const ETAG: &str = concat!("\"openfang-", env!("CARGO_PKG_VERSION"), "\"");
/// Embedded logo PNG for single-binary deployment.
const LOGO_PNG: &[u8] = include_bytes!("../static/logo.png");
/// Embedded favicon ICO for browser tabs.
const FAVICON_ICO: &[u8] = include_bytes!("../static/favicon.ico");
/// GET /logo.png — Serve the OpenFang logo.
pub async fn logo_png() -> impl IntoResponse {
(
[
(header::CONTENT_TYPE, "image/png"),
(header::CACHE_CONTROL, "public, max-age=86400, immutable"),
],
LOGO_PNG,
)
}
/// GET /favicon.ico — Serve the OpenFang favicon.
pub async fn favicon_ico() -> impl IntoResponse {
(
[
(header::CONTENT_TYPE, "image/x-icon"),
(header::CACHE_CONTROL, "public, max-age=86400, immutable"),
],
FAVICON_ICO,
)
}
/// GET / — Serve the OpenFang Dashboard single-page application.
///
/// Returns the full SPA with ETag header based on package version for caching.
pub async fn webchat_page() -> impl IntoResponse {
(
[
(header::CONTENT_TYPE, "text/html; charset=utf-8"),
(header::ETAG, ETAG),
(
header::CACHE_CONTROL,
"public, max-age=3600, must-revalidate",
),
],
WEBCHAT_HTML,
)
}
/// The embedded HTML/CSS/JS for the OpenFang Dashboard.
///
/// Assembled at compile time from organized static files.
/// All vendor libraries (Alpine.js, marked.js, highlight.js) are bundled
/// locally — no CDN dependency. Alpine.js is included LAST because it
/// immediately processes x-data directives and fires alpine:init on load.
const WEBCHAT_HTML: &str = concat!(
include_str!("../static/index_head.html"),
"<style>\n",
include_str!("../static/css/theme.css"),
"\n",
include_str!("../static/css/layout.css"),
"\n",
include_str!("../static/css/components.css"),
"\n",
include_str!("../static/vendor/github-dark.min.css"),
"\n</style>\n",
include_str!("../static/index_body.html"),
// Vendor libs: marked + highlight first (used by app.js)
"<script>\n",
include_str!("../static/vendor/marked.min.js"),
"\n</script>\n",
"<script>\n",
include_str!("../static/vendor/highlight.min.js"),
"\n</script>\n",
// App code
"<script>\n",
include_str!("../static/js/api.js"),
"\n",
include_str!("../static/js/app.js"),
"\n",
include_str!("../static/js/pages/overview.js"),
"\n",
include_str!("../static/js/pages/chat.js"),
"\n",
include_str!("../static/js/pages/agents.js"),
"\n",
include_str!("../static/js/pages/workflows.js"),
"\n",
include_str!("../static/js/pages/workflow-builder.js"),
"\n",
include_str!("../static/js/pages/channels.js"),
"\n",
include_str!("../static/js/pages/skills.js"),
"\n",
include_str!("../static/js/pages/hands.js"),
"\n",
include_str!("../static/js/pages/scheduler.js"),
"\n",
include_str!("../static/js/pages/settings.js"),
"\n",
include_str!("../static/js/pages/usage.js"),
"\n",
include_str!("../static/js/pages/sessions.js"),
"\n",
include_str!("../static/js/pages/logs.js"),
"\n",
include_str!("../static/js/pages/wizard.js"),
"\n",
include_str!("../static/js/pages/approvals.js"),
"\n</script>\n",
// Alpine.js MUST be last — it processes x-data and fires alpine:init
"<script>\n",
include_str!("../static/vendor/alpine.min.js"),
"\n</script>\n",
"</body></html>"
);