somratpro Claude Sonnet 4.6 commited on
Commit
939f343
Β·
1 Parent(s): e778d47

feat: custom dashboard at / and route DeerFlow under /app

Browse files

- Add dashboard.html: status page with links to workspace, setup, API docs
- nginx: location = / serves dashboard.html (alias, no-cache)
- nginx: location = /app redirects 302 β†’ /workspace
- Dockerfile: COPY dashboard.html to /app/dashboard.html

To use DeerFlow: visit /workspace (or /app as alias).
First boot: visit /setup to create admin account before /workspace.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (3) hide show
  1. Dockerfile +1 -0
  2. dashboard.html +308 -0
  3. nginx.conf +13 -1
Dockerfile CHANGED
@@ -96,6 +96,7 @@ COPY --from=source --chown=1000:1000 /src/config.example.yaml /app/config.exampl
96
 
97
  # ── Copy HuggingFlow runtime scripts ─────────────────────────────
98
  COPY --chown=1000:1000 nginx.conf /etc/nginx/nginx.conf
 
99
  COPY --chown=1000:1000 start.sh /app/start.sh
100
  COPY --chown=1000:1000 deerflow-sync.py /app/deerflow-sync.py
101
  COPY --chown=1000:1000 health-server.js /app/health-server.js
 
96
 
97
  # ── Copy HuggingFlow runtime scripts ─────────────────────────────
98
  COPY --chown=1000:1000 nginx.conf /etc/nginx/nginx.conf
99
+ COPY --chown=1000:1000 dashboard.html /app/dashboard.html
100
  COPY --chown=1000:1000 start.sh /app/start.sh
101
  COPY --chown=1000:1000 deerflow-sync.py /app/deerflow-sync.py
102
  COPY --chown=1000:1000 health-server.js /app/health-server.js
dashboard.html ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>HuggingFlow β€” DeerFlow on Hugging Face</title>
7
+ <style>
8
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
9
+
10
+ :root {
11
+ --bg: #0f1117;
12
+ --surface: #1a1d27;
13
+ --border: #2a2d3e;
14
+ --accent: #ff9d00;
15
+ --accent2: #7c3aed;
16
+ --text: #e2e8f0;
17
+ --muted: #64748b;
18
+ --green: #22c55e;
19
+ --card: #161927;
20
+ }
21
+
22
+ body {
23
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
24
+ background: var(--bg);
25
+ color: var(--text);
26
+ min-height: 100vh;
27
+ display: flex;
28
+ flex-direction: column;
29
+ align-items: center;
30
+ justify-content: center;
31
+ padding: 2rem 1rem;
32
+ }
33
+
34
+ .container { max-width: 720px; width: 100%; }
35
+
36
+ /* ── Header ─────────────────────────────────────────── */
37
+ .header {
38
+ text-align: center;
39
+ margin-bottom: 3rem;
40
+ }
41
+
42
+ .logo {
43
+ font-size: 3.5rem;
44
+ line-height: 1;
45
+ margin-bottom: 0.75rem;
46
+ filter: drop-shadow(0 0 24px rgba(255,157,0,0.4));
47
+ }
48
+
49
+ .title {
50
+ font-size: 2rem;
51
+ font-weight: 700;
52
+ letter-spacing: -0.02em;
53
+ background: linear-gradient(135deg, #ff9d00 0%, #ff6b35 50%, #7c3aed 100%);
54
+ -webkit-background-clip: text;
55
+ -webkit-text-fill-color: transparent;
56
+ background-clip: text;
57
+ margin-bottom: 0.5rem;
58
+ }
59
+
60
+ .subtitle {
61
+ color: var(--muted);
62
+ font-size: 0.95rem;
63
+ }
64
+
65
+ .subtitle a {
66
+ color: var(--accent);
67
+ text-decoration: none;
68
+ }
69
+
70
+ .subtitle a:hover { text-decoration: underline; }
71
+
72
+ /* ── Status pill ────────────────────────────────────── */
73
+ .status-pill {
74
+ display: inline-flex;
75
+ align-items: center;
76
+ gap: 0.5rem;
77
+ background: rgba(34,197,94,0.1);
78
+ border: 1px solid rgba(34,197,94,0.3);
79
+ color: var(--green);
80
+ font-size: 0.8rem;
81
+ font-weight: 600;
82
+ padding: 0.3rem 0.85rem;
83
+ border-radius: 999px;
84
+ margin-bottom: 2rem;
85
+ letter-spacing: 0.04em;
86
+ text-transform: uppercase;
87
+ }
88
+
89
+ .dot {
90
+ width: 7px; height: 7px;
91
+ background: var(--green);
92
+ border-radius: 50%;
93
+ animation: pulse 2s ease-in-out infinite;
94
+ }
95
+
96
+ @keyframes pulse {
97
+ 0%,100% { opacity: 1; transform: scale(1); }
98
+ 50% { opacity: 0.5; transform: scale(0.8); }
99
+ }
100
+
101
+ /* ── Primary CTA ────────────────────────────────────── */
102
+ .cta-row {
103
+ display: flex;
104
+ gap: 0.75rem;
105
+ justify-content: center;
106
+ margin-bottom: 2.5rem;
107
+ flex-wrap: wrap;
108
+ }
109
+
110
+ .btn {
111
+ display: inline-flex;
112
+ align-items: center;
113
+ gap: 0.45rem;
114
+ font-size: 0.9rem;
115
+ font-weight: 600;
116
+ padding: 0.65rem 1.4rem;
117
+ border-radius: 8px;
118
+ text-decoration: none;
119
+ transition: all 0.15s ease;
120
+ border: 1px solid transparent;
121
+ }
122
+
123
+ .btn-primary {
124
+ background: linear-gradient(135deg, #ff9d00, #ff6b35);
125
+ color: #fff;
126
+ box-shadow: 0 4px 16px rgba(255,157,0,0.3);
127
+ }
128
+
129
+ .btn-primary:hover {
130
+ transform: translateY(-1px);
131
+ box-shadow: 0 6px 24px rgba(255,157,0,0.45);
132
+ }
133
+
134
+ .btn-ghost {
135
+ background: var(--surface);
136
+ color: var(--text);
137
+ border-color: var(--border);
138
+ }
139
+
140
+ .btn-ghost:hover {
141
+ background: var(--border);
142
+ transform: translateY(-1px);
143
+ }
144
+
145
+ /* ── Info grid ──────────────────────────────────────── */
146
+ .grid {
147
+ display: grid;
148
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
149
+ gap: 1rem;
150
+ margin-bottom: 2rem;
151
+ }
152
+
153
+ .card {
154
+ background: var(--card);
155
+ border: 1px solid var(--border);
156
+ border-radius: 12px;
157
+ padding: 1.25rem 1.5rem;
158
+ }
159
+
160
+ .card-label {
161
+ font-size: 0.7rem;
162
+ font-weight: 600;
163
+ text-transform: uppercase;
164
+ letter-spacing: 0.08em;
165
+ color: var(--muted);
166
+ margin-bottom: 0.5rem;
167
+ }
168
+
169
+ .card-value {
170
+ font-size: 1rem;
171
+ font-weight: 600;
172
+ color: var(--text);
173
+ }
174
+
175
+ .card-value.mono { font-family: "SF Mono", "Fira Code", monospace; font-size: 0.85rem; }
176
+
177
+ /* ── Routes table ───────────────────────────────────── */
178
+ .routes {
179
+ background: var(--card);
180
+ border: 1px solid var(--border);
181
+ border-radius: 12px;
182
+ overflow: hidden;
183
+ margin-bottom: 2rem;
184
+ }
185
+
186
+ .routes-header {
187
+ padding: 0.85rem 1.5rem;
188
+ font-size: 0.75rem;
189
+ font-weight: 600;
190
+ text-transform: uppercase;
191
+ letter-spacing: 0.08em;
192
+ color: var(--muted);
193
+ border-bottom: 1px solid var(--border);
194
+ }
195
+
196
+ .route-row {
197
+ display: flex;
198
+ align-items: center;
199
+ justify-content: space-between;
200
+ padding: 0.85rem 1.5rem;
201
+ border-bottom: 1px solid var(--border);
202
+ text-decoration: none;
203
+ color: var(--text);
204
+ transition: background 0.1s;
205
+ }
206
+
207
+ .route-row:last-child { border-bottom: none; }
208
+
209
+ .route-row:hover { background: var(--border); }
210
+
211
+ .route-path {
212
+ font-family: "SF Mono", "Fira Code", monospace;
213
+ font-size: 0.85rem;
214
+ color: var(--accent);
215
+ }
216
+
217
+ .route-desc { font-size: 0.85rem; color: var(--muted); }
218
+
219
+ .route-arrow { color: var(--muted); font-size: 0.8rem; }
220
+
221
+ /* ── Footer ─────────────────────────────────────────── */
222
+ .footer {
223
+ text-align: center;
224
+ font-size: 0.78rem;
225
+ color: var(--muted);
226
+ }
227
+
228
+ .footer a { color: var(--muted); }
229
+ .footer a:hover { color: var(--text); }
230
+ </style>
231
+ </head>
232
+ <body>
233
+ <div class="container">
234
+ <div class="header">
235
+ <div class="logo">🦌</div>
236
+ <div class="title">HuggingFlow</div>
237
+ <p class="subtitle">
238
+ <a href="https://github.com/bytedance/deer-flow" target="_blank" rel="noopener">DeerFlow</a>
239
+ research agent Β· hosted on Hugging Face Spaces
240
+ </p>
241
+ </div>
242
+
243
+ <div style="text-align:center">
244
+ <span class="status-pill"><span class="dot"></span>Running</span>
245
+ </div>
246
+
247
+ <div class="cta-row">
248
+ <a href="/workspace" class="btn btn-primary">⚑ Open Workspace</a>
249
+ <a href="/setup" class="btn btn-ghost">πŸ”§ Admin Setup</a>
250
+ <a href="/docs" class="btn btn-ghost">πŸ“– API Docs</a>
251
+ </div>
252
+
253
+ <div class="grid">
254
+ <div class="card">
255
+ <div class="card-label">Stack</div>
256
+ <div class="card-value">DeerFlow v2</div>
257
+ </div>
258
+ <div class="card">
259
+ <div class="card-label">Backend</div>
260
+ <div class="card-value mono">FastAPI Β· port 8001</div>
261
+ </div>
262
+ <div class="card">
263
+ <div class="card-label">Frontend</div>
264
+ <div class="card-value mono">Next.js Β· port 3000</div>
265
+ </div>
266
+ <div class="card">
267
+ <div class="card-label">Proxy</div>
268
+ <div class="card-value mono">nginx Β· port 7861</div>
269
+ </div>
270
+ </div>
271
+
272
+ <div class="routes">
273
+ <div class="routes-header">Available Routes</div>
274
+ <a href="/" class="route-row">
275
+ <span class="route-path">/</span>
276
+ <span class="route-desc">This dashboard</span>
277
+ <span class="route-arrow">β†’</span>
278
+ </a>
279
+ <a href="/workspace" class="route-row">
280
+ <span class="route-path">/workspace</span>
281
+ <span class="route-desc">DeerFlow research workspace</span>
282
+ <span class="route-arrow">β†’</span>
283
+ </a>
284
+ <a href="/setup" class="route-row">
285
+ <span class="route-path">/setup</span>
286
+ <span class="route-desc">Admin account creation (first boot)</span>
287
+ <span class="route-arrow">β†’</span>
288
+ </a>
289
+ <a href="/api/health" class="route-row">
290
+ <span class="route-path">/api/health</span>
291
+ <span class="route-desc">Backend health check (JSON)</span>
292
+ <span class="route-arrow">β†’</span>
293
+ </a>
294
+ <a href="/docs" class="route-row">
295
+ <span class="route-path">/docs</span>
296
+ <span class="route-desc">Swagger API reference</span>
297
+ <span class="route-arrow">β†’</span>
298
+ </a>
299
+ </div>
300
+
301
+ <p class="footer">
302
+ <a href="https://github.com/somratpro/HuggingFlow" target="_blank" rel="noopener">HuggingFlow</a>
303
+ Β· MIT License Β·
304
+ <a href="https://github.com/bytedance/deer-flow" target="_blank" rel="noopener">DeerFlow by ByteDance</a>
305
+ </p>
306
+ </div>
307
+ </body>
308
+ </html>
nginx.conf CHANGED
@@ -120,7 +120,19 @@ http {
120
  proxy_read_timeout 600s;
121
  }
122
 
123
- # ── All other requests β†’ Next.js frontend ─────────────────
 
 
 
 
 
 
 
 
 
 
 
 
124
  location / {
125
  proxy_pass http://127.0.0.1:3000;
126
  proxy_http_version 1.1;
 
120
  proxy_read_timeout 600s;
121
  }
122
 
123
+ # ── Custom dashboard at root ──────────────────────────
124
+ location = / {
125
+ alias /app/dashboard.html;
126
+ default_type text/html;
127
+ add_header Cache-Control "no-cache";
128
+ }
129
+
130
+ # ── /app β†’ DeerFlow workspace (convenience alias) ─────
131
+ location = /app {
132
+ return 302 /workspace;
133
+ }
134
+
135
+ # ── All other requests β†’ Next.js frontend ─────────────
136
  location / {
137
  proxy_pass http://127.0.0.1:3000;
138
  proxy_http_version 1.1;