jskelet 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/AGENTS.md +136 -136
  2. package/CHANGELOG.md +620 -596
  3. package/LICENSE +21 -21
  4. package/bin/jskelet.mjs +130 -130
  5. package/docs/01-baslangic.md +291 -291
  6. package/docs/02-mimari.md +310 -309
  7. package/docs/03-routing.md +515 -515
  8. package/docs/04-render-ve-sablonlar.md +661 -661
  9. package/docs/05-islands.md +486 -486
  10. package/docs/06-cache.md +1443 -1423
  11. package/docs/07-yapilandirma.md +12 -6
  12. package/docs/08-build.md +429 -428
  13. package/docs/09-dev-araclari.md +364 -364
  14. package/docs/10-dagitim.md +338 -338
  15. package/docs/12-panel-ve-oturum.md +478 -478
  16. package/docs/README.md +83 -83
  17. package/docs/en/01-getting-started.md +298 -298
  18. package/docs/en/02-architecture.md +329 -328
  19. package/docs/en/03-routing.md +531 -531
  20. package/docs/en/04-rendering.md +669 -669
  21. package/docs/en/05-islands.md +497 -497
  22. package/docs/en/06-caching.md +1453 -1431
  23. package/docs/en/07-configuration.md +1219 -1214
  24. package/docs/en/08-build.md +447 -446
  25. package/docs/en/09-dev-tools.md +373 -373
  26. package/docs/en/10-deployment.md +340 -340
  27. package/docs/en/11-migration.md +398 -398
  28. package/docs/en/12-dashboards-and-sessions.md +488 -488
  29. package/docs/en/README.md +87 -87
  30. package/package.json +137 -137
  31. package/src/build/ensure-build.mjs +19 -19
  32. package/src/build/paths.mjs +153 -153
  33. package/src/build/resolve-peer.mjs +36 -36
  34. package/src/build/tasks/client.mjs +349 -349
  35. package/src/build/tasks/css.mjs +235 -235
  36. package/src/build/tasks/fonts.mjs +146 -146
  37. package/src/build/tasks/icons.mjs +357 -357
  38. package/src/build/tasks/images.mjs +244 -244
  39. package/src/build/tasks/precompress.mjs +78 -78
  40. package/src/build/tasks/templates.mjs +20 -20
  41. package/src/client/admin/i18n.js +764 -764
  42. package/src/client/admin/login.html +74 -74
  43. package/src/client/admin/panel.css +809 -809
  44. package/src/client/admin/panel.html +495 -495
  45. package/src/client/admin/panel.js +1251 -1251
  46. package/src/client/devtools/report.html +185 -185
  47. package/src/client/devtools/report.js +745 -745
  48. package/src/client/devtools/seo.js +628 -628
  49. package/src/client/dom.js +95 -95
  50. package/src/client/form.js +192 -192
  51. package/src/client/index.js +45 -45
  52. package/src/client/registry.js +305 -305
  53. package/src/client/safe-image.js +91 -91
  54. package/src/client/shared-cookie.js +225 -225
  55. package/src/client/store.js +36 -36
  56. package/src/client/swap.js +188 -188
  57. package/src/compile/codegen.js +336 -336
  58. package/src/compile/compile-all.js +149 -149
  59. package/src/compile/errors.js +66 -66
  60. package/src/compile/expr.js +409 -409
  61. package/src/compile/index.js +17 -17
  62. package/src/compile/parse.js +541 -541
  63. package/src/compile/resolve.js +211 -211
  64. package/src/compile/scan-exports.js +51 -51
  65. package/src/config/defaults.js +17 -1
  66. package/src/config/index.js +13 -0
  67. package/src/config/pattern.js +107 -107
  68. package/src/generate.mjs +163 -163
  69. package/src/http/control-flow.js +71 -71
  70. package/src/http/cookies-entry.js +21 -21
  71. package/src/http/cookies.js +277 -277
  72. package/src/http/request-cache.js +46 -46
  73. package/src/http/request-context.js +165 -165
  74. package/src/http/shared-cookie.js +178 -178
  75. package/src/index.js +101 -101
  76. package/src/init.mjs +230 -230
  77. package/src/migrate/apply.mjs +262 -262
  78. package/src/migrate/babel.mjs +79 -79
  79. package/src/migrate/classify.mjs +155 -155
  80. package/src/migrate/config.mjs +126 -126
  81. package/src/migrate/fs-walk.mjs +191 -191
  82. package/src/migrate/parse.mjs +26 -26
  83. package/src/migrate/scan.mjs +177 -177
  84. package/src/migrate/transform/expr-source.mjs +168 -168
  85. package/src/migrate/transform/island.mjs +67 -67
  86. package/src/migrate/transform/jsx-to-component.mjs +302 -302
  87. package/src/migrate/transform/jsx-to-jsk.mjs +330 -330
  88. package/src/migrate/transform/page-split.mjs +435 -435
  89. package/src/migrate/write.mjs +81 -81
  90. package/src/migrate.mjs +171 -171
  91. package/src/runtime/alias-hooks.mjs +119 -119
  92. package/src/runtime/register.mjs +4 -4
  93. package/src/server/admin/actions.js +229 -229
  94. package/src/server/admin/auth.js +125 -125
  95. package/src/server/admin/event-log.js +151 -151
  96. package/src/server/admin/gate.js +209 -209
  97. package/src/server/admin/inventory.js +188 -188
  98. package/src/server/admin/mount.js +56 -56
  99. package/src/server/admin/router.js +216 -216
  100. package/src/server/admin/snapshot.js +241 -241
  101. package/src/server/assets.js +147 -147
  102. package/src/server/auth/handoff.js +309 -309
  103. package/src/server/cache-blob.js +70 -0
  104. package/src/server/cache-deps.js +42 -42
  105. package/src/server/cache-vary.js +113 -113
  106. package/src/server/cloudflare.js +607 -607
  107. package/src/server/create-app.js +366 -366
  108. package/src/server/data-cache.js +553 -462
  109. package/src/server/dev/report.js +485 -485
  110. package/src/server/dev/socket.js +170 -170
  111. package/src/server/dev/version-check.mjs +139 -139
  112. package/src/server/disk-cache.js +233 -0
  113. package/src/server/ejs-adapter.js +59 -59
  114. package/src/server/html-cache.js +1196 -1122
  115. package/src/server/image-optimizer.js +500 -407
  116. package/src/server/logs/access-middleware.js +66 -66
  117. package/src/server/logs/file-sink.js +193 -66
  118. package/src/server/logs/pipeline.js +165 -158
  119. package/src/server/logs/s3-put.js +214 -214
  120. package/src/server/logs/s3-sink.js +112 -112
  121. package/src/server/metadata.js +102 -102
  122. package/src/server/middleware/compression.js +205 -205
  123. package/src/server/middleware/csrf.js +134 -134
  124. package/src/server/middleware/dev-gate.js +75 -75
  125. package/src/server/middleware/headers.js +37 -37
  126. package/src/server/middleware/redirects.js +32 -32
  127. package/src/server/middleware/robots-txt.js +341 -341
  128. package/src/server/middleware/static-precompressed.js +121 -100
  129. package/src/server/middleware/trailing-slash.js +53 -53
  130. package/src/server/middleware/upstream-proxy.js +141 -141
  131. package/src/server/og-image.js +356 -356
  132. package/src/server/port-guard.js +255 -255
  133. package/src/server/prewarm.js +1082 -1058
  134. package/src/server/redis.js +588 -569
  135. package/src/server/render.js +4 -4
  136. package/src/server/router.js +157 -157
  137. package/src/server/status-page.js +265 -265
  138. package/src/server/upstream-limiter.js +376 -376
  139. package/src/server/upstream-tracking.js +166 -166
  140. package/src/shared/cookie-domain.js +66 -66
  141. package/src/start.mjs +22 -22
  142. package/src/templates/layout.ejs +30 -30
  143. package/src/templates/layout.jsk +30 -30
  144. package/src/version.mjs +31 -31
  145. package/src/views/components/loader.js +101 -101
  146. package/src/views/helpers/html.js +102 -102
  147. package/src/views/helpers/tags.js +375 -375
  148. package/types/config/defaults.d.ts +15 -1
  149. package/types/config/index.d.ts +8 -0
  150. package/types/server/cache-blob.d.ts +13 -0
  151. package/types/server/data-cache.d.ts +9 -0
  152. package/types/server/disk-cache.d.ts +36 -0
  153. package/types/server/html-cache.d.ts +26 -3
  154. package/types/server/logs/file-sink.d.ts +16 -5
  155. package/types/server/redis.d.ts +2 -1
@@ -1,495 +1,495 @@
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" />
6
- <meta name="robots" content="noindex, nofollow, noarchive, nosnippet" />
7
- <meta name="referrer" content="no-referrer" />
8
- <title>Admin · JSkelet</title>
9
- <link rel="icon" href="logo.png" />
10
- <link rel="stylesheet" href="panel.css" />
11
- </head>
12
- <body>
13
- <header>
14
- <div class="brand">
15
- <img src="logo.png" alt="" width="30" height="30" />
16
- <span class="wordmark">JSkelet</span>
17
- <span class="badge" id="version">—</span>
18
- </div>
19
-
20
- <nav class="crumbs" aria-label="Admin" id="nav">
21
- <a href="./" data-page="overview" data-i18n="nav.overview">Overview</a>
22
- <a href="cache" data-page="cache" data-i18n="nav.cache">Cache</a>
23
- <a href="routes" data-page="routes" data-i18n="nav.routes">Routes</a>
24
- <a href="views" data-page="views" data-i18n="nav.views">Views</a>
25
- <a href="logs" data-page="logs" data-i18n="nav.logs">Logs</a>
26
- <a href="system" data-page="system" data-i18n="nav.system">System</a>
27
- </nav>
28
-
29
- <span class="spacer"></span>
30
-
31
- <span class="chip" id="env">—</span>
32
- <span class="chip" id="pid">—</span>
33
- <span class="chip" id="uptime">—</span>
34
- <span class="chip" id="memory">—</span>
35
- <span class="chip" id="sse-status" hidden>—</span>
36
-
37
- <span id="language"></span>
38
-
39
- <label class="check">
40
- <input type="checkbox" id="auto" checked />
41
- <span data-i18n="shell.live">live</span>
42
- </label>
43
- <button id="refresh" data-i18n="shell.refresh">Refresh</button>
44
- <button id="logout" data-i18n="shell.signOut">Sign out</button>
45
- </header>
46
-
47
- <main>
48
- <!-- Overview -->
49
- <div class="page" id="page-overview" data-page="overview">
50
- <div class="cards">
51
- <div class="card">
52
- <div class="label" data-i18n="card.htmlEntries">HTML entries</div>
53
- <div class="value" id="html-size">—</div>
54
- <div class="sub" id="html-sub">—</div>
55
- </div>
56
- <div class="card">
57
- <div class="label" data-i18n="card.htmlMemory">HTML in memory</div>
58
- <div class="value" id="html-bytes">—</div>
59
- <div class="sub" id="html-stale">—</div>
60
- </div>
61
- <div class="card">
62
- <div class="label" data-i18n="card.dataEntries">Data entries</div>
63
- <div class="value" id="data-size">—</div>
64
- <div class="sub" id="data-sub">—</div>
65
- </div>
66
- <div class="card">
67
- <div class="label" data-i18n="card.sharedTier">Shared tier</div>
68
- <div class="value" id="redis-state">—</div>
69
- <div class="sub" id="redis-sub">—</div>
70
- </div>
71
- <div class="card">
72
- <div class="label" data-i18n="card.prewarm">Prewarm</div>
73
- <div class="value" id="prewarm-state">—</div>
74
- <div class="sub" id="prewarm-sub">—</div>
75
- </div>
76
- </div>
77
-
78
- <section>
79
- <h2>
80
- <span data-i18n="upstream.section">Upstream</span>
81
- <span class="count" id="upstream-heading">—</span>
82
- </h2>
83
- <div class="body">
84
- <div class="scroll">
85
- <table>
86
- <thead>
87
- <tr>
88
- <th data-i18n="upstream.host">Host</th>
89
- <th data-i18n="upstream.rate">Rate</th>
90
- <th data-i18n="upstream.state">State</th>
91
- </tr>
92
- </thead>
93
- <tbody id="upstream-tbody"></tbody>
94
- </table>
95
- <div class="empty" id="upstream-empty" data-i18n="upstream.empty" hidden>
96
- No upstream hosts tracked.
97
- </div>
98
- </div>
99
- </div>
100
- </section>
101
- </div>
102
-
103
- <!-- Cache -->
104
- <div class="page" id="page-cache" data-page="cache" hidden>
105
- <section>
106
- <h2>
107
- <span data-i18n="redis.section">Shared tier</span>
108
- <span class="count" id="redis-heading">—</span>
109
- </h2>
110
- <div class="body">
111
- <dl class="kv" id="redis-kv"></dl>
112
-
113
- <div class="row" id="redis-actions" hidden>
114
- <button id="redis-inspect" data-i18n="redis.inspect">Count keys in Redis</button>
115
- <button class="danger" id="redis-html" data-i18n="redis.dropHtml">
116
- Drop shared HTML keys
117
- </button>
118
- <button class="danger" id="redis-data" data-i18n="redis.dropData">
119
- Drop shared data keys
120
- </button>
121
- </div>
122
-
123
- <div class="suggest" id="redis-suggest" hidden>
124
- <p class="m0">
125
- <strong data-i18n="redis.suggest.title">No shared tier.</strong>
126
- <span data-i18n="redis.suggest.body"></span>
127
- </p>
128
- <pre><code>npm install ioredis</code></pre>
129
- <pre><code>// jskelet.config.mjs
130
- cache() {
131
- return {
132
- redis: { enabled: true, url: process.env.REDIS_URL },
133
- };
134
- }</code></pre>
135
- <p class="hint m0" data-i18n="redis.suggest.hint"></p>
136
- </div>
137
- </div>
138
- </section>
139
-
140
- <section>
141
- <h2>
142
- <span data-i18n="cf.section">Cloudflare</span>
143
- <span class="count" id="cf-heading">—</span>
144
- <span class="tabs">
145
- <button id="cf-refresh" data-i18n="cf.refresh">Refresh</button>
146
- </span>
147
- </h2>
148
- <div class="body">
149
- <dl class="kv" id="cf-kv"></dl>
150
-
151
- <div id="cf-tools" hidden>
152
- <div class="row">
153
- <button class="danger" id="cf-purge-all" data-i18n="cf.purgeAll">
154
- Purge everything
155
- </button>
156
- <button id="cf-purge-cached" data-i18n="cf.purgeCached">
157
- Purge URLs cached here
158
- </button>
159
- <label class="check">
160
- <input type="checkbox" id="cf-dev" />
161
- <span data-i18n="cf.devMode"></span>
162
- </label>
163
- </div>
164
-
165
- <div class="row">
166
- <select id="cf-kind">
167
- <option value="prefixes" data-i18n="cf.kind.prefixes">prefixes</option>
168
- <option value="hosts" data-i18n="cf.kind.hosts">hosts</option>
169
- <option value="tags" data-i18n="cf.kind.tags">cache tags</option>
170
- </select>
171
- <input
172
- class="grow"
173
- type="text"
174
- id="cf-values"
175
- spellcheck="false"
176
- data-i18n-ph="cf.values.placeholder"
177
- />
178
- <button class="primary" id="cf-purge-keys" data-i18n="cf.purge">Purge</button>
179
- </div>
180
-
181
- <div class="row">
182
- <button id="cf-tiered" data-i18n="cf.toggleTiered">Toggle Tiered Cache</button>
183
- <button id="cf-reserve" data-i18n="cf.toggleReserve">Toggle Cache Reserve</button>
184
- <button class="danger" id="cf-reserve-clear" data-i18n="cf.clearReserve">
185
- Clear Cache Reserve
186
- </button>
187
- </div>
188
-
189
- <div class="row">
190
- <input
191
- class="grow"
192
- type="text"
193
- id="cf-path"
194
- spellcheck="false"
195
- data-i18n-ph="cf.path.placeholder"
196
- />
197
- <button id="cf-analytics" data-i18n="cf.queryAnalytics">Query analytics</button>
198
- </div>
199
-
200
- <div id="cf-report" class="report" hidden></div>
201
- <p class="hint" data-i18n="cf.edgeHint"></p>
202
- </div>
203
-
204
- <div class="suggest" id="cf-suggest" hidden>
205
- <p class="m0">
206
- <strong data-i18n="cf.suggest.title">Cloudflare is not connected.</strong>
207
- <span data-i18n="cf.suggest.body"></span>
208
- </p>
209
- <pre><code>JSKELET_CLOUDFLARE_KEY=your_api_token
210
- JSKELET_CLOUDFLARE_ZONE_ID=your_zone_id</code></pre>
211
- <p class="hint m0" data-i18n-html="cf.suggest.hint"></p>
212
- </div>
213
- </div>
214
- </section>
215
-
216
- <section>
217
- <h2><span data-i18n="actions.section">Actions</span></h2>
218
- <div class="body">
219
- <div class="row">
220
- <input
221
- class="grow"
222
- type="text"
223
- id="target"
224
- spellcheck="false"
225
- data-i18n-ph="actions.target.placeholder"
226
- />
227
- <label class="check">
228
- <input type="checkbox" id="hard" />
229
- <span data-i18n="actions.hard"></span>
230
- </label>
231
- <button class="primary" id="invalidate" data-i18n="actions.invalidate">
232
- Invalidate
233
- </button>
234
- </div>
235
-
236
- <div class="row">
237
- <input
238
- class="grow"
239
- type="text"
240
- id="prefix"
241
- spellcheck="false"
242
- data-i18n-ph="actions.prefix.placeholder"
243
- />
244
- <button class="danger" id="clear-data" data-i18n="actions.clearData">
245
- Clear data cache
246
- </button>
247
- <button class="danger" id="clear-html" data-i18n="actions.clearHtml">
248
- Clear HTML cache
249
- </button>
250
- </div>
251
-
252
- <div class="row">
253
- <button id="prewarm" data-i18n="actions.prewarm">Prewarm all paths</button>
254
- </div>
255
-
256
- <p class="hint" data-i18n="actions.hint"></p>
257
- </div>
258
- </section>
259
-
260
- <section>
261
- <h2>
262
- <span data-i18n="entries.section">Entries</span>
263
- <span class="count" id="listed">—</span>
264
- <span class="tabs" role="tablist">
265
- <button id="tab-html" role="tab" aria-selected="true">HTML</button>
266
- <button id="tab-data" role="tab" aria-selected="false" data-i18n="tab.data">
267
- Data
268
- </button>
269
- </span>
270
- </h2>
271
- <div class="body">
272
- <div class="row">
273
- <input
274
- class="grow"
275
- type="search"
276
- id="search"
277
- spellcheck="false"
278
- data-i18n-ph="entries.search"
279
- />
280
- </div>
281
- </div>
282
- <div class="scroll">
283
- <table>
284
- <thead id="thead"></thead>
285
- <tbody id="tbody"></tbody>
286
- </table>
287
- <div class="empty" id="empty" data-i18n="entries.empty" hidden>No entries.</div>
288
- </div>
289
- </section>
290
- </div>
291
-
292
- <!-- Routes -->
293
- <div class="page" id="page-routes" data-page="routes" hidden>
294
- <section>
295
- <h2>
296
- <span data-i18n="routes.section">Registered routes</span>
297
- <span class="count" id="routes-count">—</span>
298
- </h2>
299
- <div class="scroll">
300
- <table>
301
- <thead>
302
- <tr>
303
- <th data-i18n="routes.method">Method</th>
304
- <th data-i18n="routes.path">Path</th>
305
- <th data-i18n="routes.lastStatus">Last status</th>
306
- <th data-i18n="routes.lastMs">Last ms</th>
307
- <th data-i18n="routes.hits">Hits</th>
308
- </tr>
309
- </thead>
310
- <tbody id="routes-tbody"></tbody>
311
- </table>
312
- <div class="empty" id="routes-empty" data-i18n="routes.empty" hidden>No routes.</div>
313
- </div>
314
- </section>
315
-
316
- <section>
317
- <h2>
318
- <span data-i18n="routes.modules">Route modules</span>
319
- <span class="count" id="modules-count">—</span>
320
- </h2>
321
- <div class="scroll">
322
- <table>
323
- <thead>
324
- <tr>
325
- <th data-i18n="routes.file">File</th>
326
- </tr>
327
- </thead>
328
- <tbody id="modules-tbody"></tbody>
329
- </table>
330
- </div>
331
- </section>
332
- </div>
333
-
334
- <!-- Views -->
335
- <div class="page" id="page-views" data-page="views" hidden>
336
- <section>
337
- <h2>
338
- <span data-i18n="views.section">Views</span>
339
- <span class="count" id="views-count">—</span>
340
- </h2>
341
- <div class="body">
342
- <div class="row">
343
- <input
344
- class="grow"
345
- type="search"
346
- id="views-search"
347
- spellcheck="false"
348
- data-i18n-ph="views.search"
349
- />
350
- </div>
351
- </div>
352
- <div class="scroll">
353
- <table>
354
- <thead>
355
- <tr>
356
- <th data-i18n="views.path">Path</th>
357
- <th data-i18n="views.kind">Kind</th>
358
- </tr>
359
- </thead>
360
- <tbody id="views-tbody"></tbody>
361
- </table>
362
- <div class="empty" id="views-empty" data-i18n="views.empty" hidden>No views.</div>
363
- </div>
364
- </section>
365
- </div>
366
-
367
- <!-- Logs -->
368
- <div class="page" id="page-logs" data-page="logs" hidden>
369
- <section>
370
- <h2>
371
- <span data-i18n="logs.section">Live logs</span>
372
- <span class="count" id="logs-count">—</span>
373
- </h2>
374
- <div class="body">
375
- <div class="row filters">
376
- <input
377
- class="grow"
378
- type="search"
379
- id="logs-q"
380
- spellcheck="false"
381
- data-i18n-ph="logs.search"
382
- />
383
- <select id="logs-kind">
384
- <option value="" data-i18n="logs.kind.all">all kinds</option>
385
- <option value="http">http</option>
386
- <option value="event">event</option>
387
- <option value="warn">warn</option>
388
- <option value="error">error</option>
389
- </select>
390
- <select id="logs-method">
391
- <option value="" data-i18n="logs.method.all">all methods</option>
392
- <option value="GET">GET</option>
393
- <option value="POST">POST</option>
394
- <option value="PUT">PUT</option>
395
- <option value="PATCH">PATCH</option>
396
- <option value="DELETE">DELETE</option>
397
- </select>
398
- <select id="logs-status">
399
- <option value="" data-i18n="logs.status.all">all status</option>
400
- <option value="2">2xx</option>
401
- <option value="3">3xx</option>
402
- <option value="4">4xx</option>
403
- <option value="5">5xx</option>
404
- </select>
405
- <select id="logs-cache">
406
- <option value="" data-i18n="logs.cache.all">all cache</option>
407
- <option value="HIT">HIT</option>
408
- <option value="STALE">STALE</option>
409
- <option value="MISS">MISS</option>
410
- </select>
411
- <input
412
- type="text"
413
- id="logs-path"
414
- spellcheck="false"
415
- data-i18n-ph="logs.path"
416
- style="min-width: 8rem"
417
- />
418
- <label class="check">
419
- <input type="checkbox" id="logs-pause" />
420
- <span data-i18n="logs.pause">pause</span>
421
- </label>
422
- <button id="logs-clear" data-i18n="logs.clear">Clear view</button>
423
- </div>
424
- </div>
425
- <div class="scroll log-scroll">
426
- <pre id="logs-list" class="log-list"></pre>
427
- <div class="empty" id="logs-empty" data-i18n="logs.empty" hidden>No log lines.</div>
428
- </div>
429
- </section>
430
- </div>
431
-
432
- <!-- System -->
433
- <div class="page" id="page-system" data-page="system" hidden>
434
- <section>
435
- <h2>
436
- <span data-i18n="host.section">Host</span>
437
- <span class="count" id="host-heading">—</span>
438
- </h2>
439
- <div class="body">
440
- <div class="meter">
441
- <div class="meter-head">
442
- <span data-i18n="host.cpu">CPU</span>
443
- <span class="mono" id="cpu-text">—</span>
444
- </div>
445
- <div class="bar"><span id="cpu-bar"></span></div>
446
- <p class="hint m0" id="cpu-note">—</p>
447
- </div>
448
-
449
- <div class="meter">
450
- <div class="meter-head">
451
- <span data-i18n="host.memory">Memory</span>
452
- <span class="mono" id="ram-text">—</span>
453
- </div>
454
- <div class="bar"><span id="ram-bar"></span></div>
455
- <p class="hint m0" id="ram-note">—</p>
456
- </div>
457
-
458
- <div class="meter">
459
- <div class="meter-head">
460
- <span data-i18n="host.disk">Disk</span>
461
- <span class="mono" id="disk-text">—</span>
462
- </div>
463
- <div class="bar"><span id="disk-bar"></span></div>
464
- <p class="hint m0" id="disk-note">—</p>
465
- </div>
466
- </div>
467
- </section>
468
- </div>
469
- </main>
470
-
471
- <footer>
472
- <div class="foot-brand">
473
- <img src="logo.png" alt="" width="24" height="24" />
474
- <span class="wordmark">JSkelet</span>
475
- <span class="mono" id="foot-release">—</span>
476
- </div>
477
-
478
- <div class="foot-links">
479
- <a id="foot-repo" href="https://github.com/ayberkenis/jskelet" rel="noreferrer noopener">
480
- GitHub
481
- </a>
482
- <span class="dot" aria-hidden="true">·</span>
483
- <span data-i18n="footer.panel">Admin panel</span>
484
- <span class="dot" aria-hidden="true">·</span>
485
- <span class="live">
486
- <span class="pulse"></span>
487
- <span data-i18n="footer.rotates">Password rotates on restart</span>
488
- </span>
489
- </div>
490
- </footer>
491
-
492
- <div id="toast"></div>
493
- <script type="module" src="panel.js"></script>
494
- </body>
495
- </html>
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" />
6
+ <meta name="robots" content="noindex, nofollow, noarchive, nosnippet" />
7
+ <meta name="referrer" content="no-referrer" />
8
+ <title>Admin · JSkelet</title>
9
+ <link rel="icon" href="logo.png" />
10
+ <link rel="stylesheet" href="panel.css" />
11
+ </head>
12
+ <body>
13
+ <header>
14
+ <div class="brand">
15
+ <img src="logo.png" alt="" width="30" height="30" />
16
+ <span class="wordmark">JSkelet</span>
17
+ <span class="badge" id="version">—</span>
18
+ </div>
19
+
20
+ <nav class="crumbs" aria-label="Admin" id="nav">
21
+ <a href="./" data-page="overview" data-i18n="nav.overview">Overview</a>
22
+ <a href="cache" data-page="cache" data-i18n="nav.cache">Cache</a>
23
+ <a href="routes" data-page="routes" data-i18n="nav.routes">Routes</a>
24
+ <a href="views" data-page="views" data-i18n="nav.views">Views</a>
25
+ <a href="logs" data-page="logs" data-i18n="nav.logs">Logs</a>
26
+ <a href="system" data-page="system" data-i18n="nav.system">System</a>
27
+ </nav>
28
+
29
+ <span class="spacer"></span>
30
+
31
+ <span class="chip" id="env">—</span>
32
+ <span class="chip" id="pid">—</span>
33
+ <span class="chip" id="uptime">—</span>
34
+ <span class="chip" id="memory">—</span>
35
+ <span class="chip" id="sse-status" hidden>—</span>
36
+
37
+ <span id="language"></span>
38
+
39
+ <label class="check">
40
+ <input type="checkbox" id="auto" checked />
41
+ <span data-i18n="shell.live">live</span>
42
+ </label>
43
+ <button id="refresh" data-i18n="shell.refresh">Refresh</button>
44
+ <button id="logout" data-i18n="shell.signOut">Sign out</button>
45
+ </header>
46
+
47
+ <main>
48
+ <!-- Overview -->
49
+ <div class="page" id="page-overview" data-page="overview">
50
+ <div class="cards">
51
+ <div class="card">
52
+ <div class="label" data-i18n="card.htmlEntries">HTML entries</div>
53
+ <div class="value" id="html-size">—</div>
54
+ <div class="sub" id="html-sub">—</div>
55
+ </div>
56
+ <div class="card">
57
+ <div class="label" data-i18n="card.htmlMemory">HTML in memory</div>
58
+ <div class="value" id="html-bytes">—</div>
59
+ <div class="sub" id="html-stale">—</div>
60
+ </div>
61
+ <div class="card">
62
+ <div class="label" data-i18n="card.dataEntries">Data entries</div>
63
+ <div class="value" id="data-size">—</div>
64
+ <div class="sub" id="data-sub">—</div>
65
+ </div>
66
+ <div class="card">
67
+ <div class="label" data-i18n="card.sharedTier">Shared tier</div>
68
+ <div class="value" id="redis-state">—</div>
69
+ <div class="sub" id="redis-sub">—</div>
70
+ </div>
71
+ <div class="card">
72
+ <div class="label" data-i18n="card.prewarm">Prewarm</div>
73
+ <div class="value" id="prewarm-state">—</div>
74
+ <div class="sub" id="prewarm-sub">—</div>
75
+ </div>
76
+ </div>
77
+
78
+ <section>
79
+ <h2>
80
+ <span data-i18n="upstream.section">Upstream</span>
81
+ <span class="count" id="upstream-heading">—</span>
82
+ </h2>
83
+ <div class="body">
84
+ <div class="scroll">
85
+ <table>
86
+ <thead>
87
+ <tr>
88
+ <th data-i18n="upstream.host">Host</th>
89
+ <th data-i18n="upstream.rate">Rate</th>
90
+ <th data-i18n="upstream.state">State</th>
91
+ </tr>
92
+ </thead>
93
+ <tbody id="upstream-tbody"></tbody>
94
+ </table>
95
+ <div class="empty" id="upstream-empty" data-i18n="upstream.empty" hidden>
96
+ No upstream hosts tracked.
97
+ </div>
98
+ </div>
99
+ </div>
100
+ </section>
101
+ </div>
102
+
103
+ <!-- Cache -->
104
+ <div class="page" id="page-cache" data-page="cache" hidden>
105
+ <section>
106
+ <h2>
107
+ <span data-i18n="redis.section">Shared tier</span>
108
+ <span class="count" id="redis-heading">—</span>
109
+ </h2>
110
+ <div class="body">
111
+ <dl class="kv" id="redis-kv"></dl>
112
+
113
+ <div class="row" id="redis-actions" hidden>
114
+ <button id="redis-inspect" data-i18n="redis.inspect">Count keys in Redis</button>
115
+ <button class="danger" id="redis-html" data-i18n="redis.dropHtml">
116
+ Drop shared HTML keys
117
+ </button>
118
+ <button class="danger" id="redis-data" data-i18n="redis.dropData">
119
+ Drop shared data keys
120
+ </button>
121
+ </div>
122
+
123
+ <div class="suggest" id="redis-suggest" hidden>
124
+ <p class="m0">
125
+ <strong data-i18n="redis.suggest.title">No shared tier.</strong>
126
+ <span data-i18n="redis.suggest.body"></span>
127
+ </p>
128
+ <pre><code>npm install ioredis</code></pre>
129
+ <pre><code>// jskelet.config.mjs
130
+ cache() {
131
+ return {
132
+ redis: { enabled: true, url: process.env.REDIS_URL },
133
+ };
134
+ }</code></pre>
135
+ <p class="hint m0" data-i18n="redis.suggest.hint"></p>
136
+ </div>
137
+ </div>
138
+ </section>
139
+
140
+ <section>
141
+ <h2>
142
+ <span data-i18n="cf.section">Cloudflare</span>
143
+ <span class="count" id="cf-heading">—</span>
144
+ <span class="tabs">
145
+ <button id="cf-refresh" data-i18n="cf.refresh">Refresh</button>
146
+ </span>
147
+ </h2>
148
+ <div class="body">
149
+ <dl class="kv" id="cf-kv"></dl>
150
+
151
+ <div id="cf-tools" hidden>
152
+ <div class="row">
153
+ <button class="danger" id="cf-purge-all" data-i18n="cf.purgeAll">
154
+ Purge everything
155
+ </button>
156
+ <button id="cf-purge-cached" data-i18n="cf.purgeCached">
157
+ Purge URLs cached here
158
+ </button>
159
+ <label class="check">
160
+ <input type="checkbox" id="cf-dev" />
161
+ <span data-i18n="cf.devMode"></span>
162
+ </label>
163
+ </div>
164
+
165
+ <div class="row">
166
+ <select id="cf-kind">
167
+ <option value="prefixes" data-i18n="cf.kind.prefixes">prefixes</option>
168
+ <option value="hosts" data-i18n="cf.kind.hosts">hosts</option>
169
+ <option value="tags" data-i18n="cf.kind.tags">cache tags</option>
170
+ </select>
171
+ <input
172
+ class="grow"
173
+ type="text"
174
+ id="cf-values"
175
+ spellcheck="false"
176
+ data-i18n-ph="cf.values.placeholder"
177
+ />
178
+ <button class="primary" id="cf-purge-keys" data-i18n="cf.purge">Purge</button>
179
+ </div>
180
+
181
+ <div class="row">
182
+ <button id="cf-tiered" data-i18n="cf.toggleTiered">Toggle Tiered Cache</button>
183
+ <button id="cf-reserve" data-i18n="cf.toggleReserve">Toggle Cache Reserve</button>
184
+ <button class="danger" id="cf-reserve-clear" data-i18n="cf.clearReserve">
185
+ Clear Cache Reserve
186
+ </button>
187
+ </div>
188
+
189
+ <div class="row">
190
+ <input
191
+ class="grow"
192
+ type="text"
193
+ id="cf-path"
194
+ spellcheck="false"
195
+ data-i18n-ph="cf.path.placeholder"
196
+ />
197
+ <button id="cf-analytics" data-i18n="cf.queryAnalytics">Query analytics</button>
198
+ </div>
199
+
200
+ <div id="cf-report" class="report" hidden></div>
201
+ <p class="hint" data-i18n="cf.edgeHint"></p>
202
+ </div>
203
+
204
+ <div class="suggest" id="cf-suggest" hidden>
205
+ <p class="m0">
206
+ <strong data-i18n="cf.suggest.title">Cloudflare is not connected.</strong>
207
+ <span data-i18n="cf.suggest.body"></span>
208
+ </p>
209
+ <pre><code>JSKELET_CLOUDFLARE_KEY=your_api_token
210
+ JSKELET_CLOUDFLARE_ZONE_ID=your_zone_id</code></pre>
211
+ <p class="hint m0" data-i18n-html="cf.suggest.hint"></p>
212
+ </div>
213
+ </div>
214
+ </section>
215
+
216
+ <section>
217
+ <h2><span data-i18n="actions.section">Actions</span></h2>
218
+ <div class="body">
219
+ <div class="row">
220
+ <input
221
+ class="grow"
222
+ type="text"
223
+ id="target"
224
+ spellcheck="false"
225
+ data-i18n-ph="actions.target.placeholder"
226
+ />
227
+ <label class="check">
228
+ <input type="checkbox" id="hard" />
229
+ <span data-i18n="actions.hard"></span>
230
+ </label>
231
+ <button class="primary" id="invalidate" data-i18n="actions.invalidate">
232
+ Invalidate
233
+ </button>
234
+ </div>
235
+
236
+ <div class="row">
237
+ <input
238
+ class="grow"
239
+ type="text"
240
+ id="prefix"
241
+ spellcheck="false"
242
+ data-i18n-ph="actions.prefix.placeholder"
243
+ />
244
+ <button class="danger" id="clear-data" data-i18n="actions.clearData">
245
+ Clear data cache
246
+ </button>
247
+ <button class="danger" id="clear-html" data-i18n="actions.clearHtml">
248
+ Clear HTML cache
249
+ </button>
250
+ </div>
251
+
252
+ <div class="row">
253
+ <button id="prewarm" data-i18n="actions.prewarm">Prewarm all paths</button>
254
+ </div>
255
+
256
+ <p class="hint" data-i18n="actions.hint"></p>
257
+ </div>
258
+ </section>
259
+
260
+ <section>
261
+ <h2>
262
+ <span data-i18n="entries.section">Entries</span>
263
+ <span class="count" id="listed">—</span>
264
+ <span class="tabs" role="tablist">
265
+ <button id="tab-html" role="tab" aria-selected="true">HTML</button>
266
+ <button id="tab-data" role="tab" aria-selected="false" data-i18n="tab.data">
267
+ Data
268
+ </button>
269
+ </span>
270
+ </h2>
271
+ <div class="body">
272
+ <div class="row">
273
+ <input
274
+ class="grow"
275
+ type="search"
276
+ id="search"
277
+ spellcheck="false"
278
+ data-i18n-ph="entries.search"
279
+ />
280
+ </div>
281
+ </div>
282
+ <div class="scroll">
283
+ <table>
284
+ <thead id="thead"></thead>
285
+ <tbody id="tbody"></tbody>
286
+ </table>
287
+ <div class="empty" id="empty" data-i18n="entries.empty" hidden>No entries.</div>
288
+ </div>
289
+ </section>
290
+ </div>
291
+
292
+ <!-- Routes -->
293
+ <div class="page" id="page-routes" data-page="routes" hidden>
294
+ <section>
295
+ <h2>
296
+ <span data-i18n="routes.section">Registered routes</span>
297
+ <span class="count" id="routes-count">—</span>
298
+ </h2>
299
+ <div class="scroll">
300
+ <table>
301
+ <thead>
302
+ <tr>
303
+ <th data-i18n="routes.method">Method</th>
304
+ <th data-i18n="routes.path">Path</th>
305
+ <th data-i18n="routes.lastStatus">Last status</th>
306
+ <th data-i18n="routes.lastMs">Last ms</th>
307
+ <th data-i18n="routes.hits">Hits</th>
308
+ </tr>
309
+ </thead>
310
+ <tbody id="routes-tbody"></tbody>
311
+ </table>
312
+ <div class="empty" id="routes-empty" data-i18n="routes.empty" hidden>No routes.</div>
313
+ </div>
314
+ </section>
315
+
316
+ <section>
317
+ <h2>
318
+ <span data-i18n="routes.modules">Route modules</span>
319
+ <span class="count" id="modules-count">—</span>
320
+ </h2>
321
+ <div class="scroll">
322
+ <table>
323
+ <thead>
324
+ <tr>
325
+ <th data-i18n="routes.file">File</th>
326
+ </tr>
327
+ </thead>
328
+ <tbody id="modules-tbody"></tbody>
329
+ </table>
330
+ </div>
331
+ </section>
332
+ </div>
333
+
334
+ <!-- Views -->
335
+ <div class="page" id="page-views" data-page="views" hidden>
336
+ <section>
337
+ <h2>
338
+ <span data-i18n="views.section">Views</span>
339
+ <span class="count" id="views-count">—</span>
340
+ </h2>
341
+ <div class="body">
342
+ <div class="row">
343
+ <input
344
+ class="grow"
345
+ type="search"
346
+ id="views-search"
347
+ spellcheck="false"
348
+ data-i18n-ph="views.search"
349
+ />
350
+ </div>
351
+ </div>
352
+ <div class="scroll">
353
+ <table>
354
+ <thead>
355
+ <tr>
356
+ <th data-i18n="views.path">Path</th>
357
+ <th data-i18n="views.kind">Kind</th>
358
+ </tr>
359
+ </thead>
360
+ <tbody id="views-tbody"></tbody>
361
+ </table>
362
+ <div class="empty" id="views-empty" data-i18n="views.empty" hidden>No views.</div>
363
+ </div>
364
+ </section>
365
+ </div>
366
+
367
+ <!-- Logs -->
368
+ <div class="page" id="page-logs" data-page="logs" hidden>
369
+ <section>
370
+ <h2>
371
+ <span data-i18n="logs.section">Live logs</span>
372
+ <span class="count" id="logs-count">—</span>
373
+ </h2>
374
+ <div class="body">
375
+ <div class="row filters">
376
+ <input
377
+ class="grow"
378
+ type="search"
379
+ id="logs-q"
380
+ spellcheck="false"
381
+ data-i18n-ph="logs.search"
382
+ />
383
+ <select id="logs-kind">
384
+ <option value="" data-i18n="logs.kind.all">all kinds</option>
385
+ <option value="http">http</option>
386
+ <option value="event">event</option>
387
+ <option value="warn">warn</option>
388
+ <option value="error">error</option>
389
+ </select>
390
+ <select id="logs-method">
391
+ <option value="" data-i18n="logs.method.all">all methods</option>
392
+ <option value="GET">GET</option>
393
+ <option value="POST">POST</option>
394
+ <option value="PUT">PUT</option>
395
+ <option value="PATCH">PATCH</option>
396
+ <option value="DELETE">DELETE</option>
397
+ </select>
398
+ <select id="logs-status">
399
+ <option value="" data-i18n="logs.status.all">all status</option>
400
+ <option value="2">2xx</option>
401
+ <option value="3">3xx</option>
402
+ <option value="4">4xx</option>
403
+ <option value="5">5xx</option>
404
+ </select>
405
+ <select id="logs-cache">
406
+ <option value="" data-i18n="logs.cache.all">all cache</option>
407
+ <option value="HIT">HIT</option>
408
+ <option value="STALE">STALE</option>
409
+ <option value="MISS">MISS</option>
410
+ </select>
411
+ <input
412
+ type="text"
413
+ id="logs-path"
414
+ spellcheck="false"
415
+ data-i18n-ph="logs.path"
416
+ style="min-width: 8rem"
417
+ />
418
+ <label class="check">
419
+ <input type="checkbox" id="logs-pause" />
420
+ <span data-i18n="logs.pause">pause</span>
421
+ </label>
422
+ <button id="logs-clear" data-i18n="logs.clear">Clear view</button>
423
+ </div>
424
+ </div>
425
+ <div class="scroll log-scroll">
426
+ <pre id="logs-list" class="log-list"></pre>
427
+ <div class="empty" id="logs-empty" data-i18n="logs.empty" hidden>No log lines.</div>
428
+ </div>
429
+ </section>
430
+ </div>
431
+
432
+ <!-- System -->
433
+ <div class="page" id="page-system" data-page="system" hidden>
434
+ <section>
435
+ <h2>
436
+ <span data-i18n="host.section">Host</span>
437
+ <span class="count" id="host-heading">—</span>
438
+ </h2>
439
+ <div class="body">
440
+ <div class="meter">
441
+ <div class="meter-head">
442
+ <span data-i18n="host.cpu">CPU</span>
443
+ <span class="mono" id="cpu-text">—</span>
444
+ </div>
445
+ <div class="bar"><span id="cpu-bar"></span></div>
446
+ <p class="hint m0" id="cpu-note">—</p>
447
+ </div>
448
+
449
+ <div class="meter">
450
+ <div class="meter-head">
451
+ <span data-i18n="host.memory">Memory</span>
452
+ <span class="mono" id="ram-text">—</span>
453
+ </div>
454
+ <div class="bar"><span id="ram-bar"></span></div>
455
+ <p class="hint m0" id="ram-note">—</p>
456
+ </div>
457
+
458
+ <div class="meter">
459
+ <div class="meter-head">
460
+ <span data-i18n="host.disk">Disk</span>
461
+ <span class="mono" id="disk-text">—</span>
462
+ </div>
463
+ <div class="bar"><span id="disk-bar"></span></div>
464
+ <p class="hint m0" id="disk-note">—</p>
465
+ </div>
466
+ </div>
467
+ </section>
468
+ </div>
469
+ </main>
470
+
471
+ <footer>
472
+ <div class="foot-brand">
473
+ <img src="logo.png" alt="" width="24" height="24" />
474
+ <span class="wordmark">JSkelet</span>
475
+ <span class="mono" id="foot-release">—</span>
476
+ </div>
477
+
478
+ <div class="foot-links">
479
+ <a id="foot-repo" href="https://github.com/ayberkenis/jskelet" rel="noreferrer noopener">
480
+ GitHub
481
+ </a>
482
+ <span class="dot" aria-hidden="true">·</span>
483
+ <span data-i18n="footer.panel">Admin panel</span>
484
+ <span class="dot" aria-hidden="true">·</span>
485
+ <span class="live">
486
+ <span class="pulse"></span>
487
+ <span data-i18n="footer.rotates">Password rotates on restart</span>
488
+ </span>
489
+ </div>
490
+ </footer>
491
+
492
+ <div id="toast"></div>
493
+ <script type="module" src="panel.js"></script>
494
+ </body>
495
+ </html>