mcpmake 0.1.0
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.
- package/README.md +691 -0
- package/bin/mcpmake.mjs +2 -0
- package/dist/analyzer/auth-detector.d.ts +12 -0
- package/dist/analyzer/auth-detector.js +142 -0
- package/dist/analyzer/dom-parser.d.ts +10 -0
- package/dist/analyzer/dom-parser.js +259 -0
- package/dist/analyzer/goal-crawler.d.ts +25 -0
- package/dist/analyzer/goal-crawler.js +177 -0
- package/dist/analyzer/hybrid-detector.d.ts +28 -0
- package/dist/analyzer/hybrid-detector.js +96 -0
- package/dist/analyzer/index.d.ts +12 -0
- package/dist/analyzer/index.js +8 -0
- package/dist/analyzer/screenshot-capture.d.ts +29 -0
- package/dist/analyzer/screenshot-capture.js +42 -0
- package/dist/analyzer/selector-builder.d.ts +19 -0
- package/dist/analyzer/selector-builder.js +199 -0
- package/dist/analyzer/semantic-analyzer.d.ts +13 -0
- package/dist/analyzer/semantic-analyzer.js +145 -0
- package/dist/analyzer/site-crawler.d.ts +38 -0
- package/dist/analyzer/site-crawler.js +235 -0
- package/dist/cloud/billing/billing-engine.d.ts +44 -0
- package/dist/cloud/billing/billing-engine.js +81 -0
- package/dist/cloud/billing/credit-store.d.ts +64 -0
- package/dist/cloud/billing/credit-store.js +168 -0
- package/dist/cloud/billing/index.d.ts +4 -0
- package/dist/cloud/billing/index.js +2 -0
- package/dist/cloud/billing/usage-store.d.ts +42 -0
- package/dist/cloud/billing/usage-store.js +85 -0
- package/dist/cloud/billing/usage-tracker.d.ts +38 -0
- package/dist/cloud/billing/usage-tracker.js +95 -0
- package/dist/cloud/build-pipeline.d.ts +39 -0
- package/dist/cloud/build-pipeline.js +310 -0
- package/dist/cloud/build-queue.d.ts +30 -0
- package/dist/cloud/build-queue.js +70 -0
- package/dist/cloud/caddy-manager.d.ts +18 -0
- package/dist/cloud/caddy-manager.js +97 -0
- package/dist/cloud/container-backend.d.ts +62 -0
- package/dist/cloud/container-backend.js +59 -0
- package/dist/cloud/container-manager.d.ts +64 -0
- package/dist/cloud/container-manager.js +301 -0
- package/dist/cloud/crypto.d.ts +27 -0
- package/dist/cloud/crypto.js +63 -0
- package/dist/cloud/db/index.d.ts +27 -0
- package/dist/cloud/db/index.js +53 -0
- package/dist/cloud/db/migrations.d.ts +12 -0
- package/dist/cloud/db/migrations.js +329 -0
- package/dist/cloud/db/pg-store.d.ts +45 -0
- package/dist/cloud/db/pg-store.js +336 -0
- package/dist/cloud/failure-tracker.d.ts +51 -0
- package/dist/cloud/failure-tracker.js +102 -0
- package/dist/cloud/idle-monitor.d.ts +30 -0
- package/dist/cloud/idle-monitor.js +70 -0
- package/dist/cloud/mailer.d.ts +21 -0
- package/dist/cloud/mailer.js +193 -0
- package/dist/cloud/mcp-proxy.d.ts +58 -0
- package/dist/cloud/mcp-proxy.js +203 -0
- package/dist/cloud/metric-samples.d.ts +43 -0
- package/dist/cloud/metric-samples.js +85 -0
- package/dist/cloud/metrics.d.ts +26 -0
- package/dist/cloud/metrics.js +59 -0
- package/dist/cloud/multipart.d.ts +26 -0
- package/dist/cloud/multipart.js +132 -0
- package/dist/cloud/observability.d.ts +27 -0
- package/dist/cloud/observability.js +98 -0
- package/dist/cloud/rate-limiter.d.ts +31 -0
- package/dist/cloud/rate-limiter.js +58 -0
- package/dist/cloud/request-security.d.ts +5 -0
- package/dist/cloud/request-security.js +74 -0
- package/dist/cloud/resource-monitor.d.ts +69 -0
- package/dist/cloud/resource-monitor.js +130 -0
- package/dist/cloud/secret-store.d.ts +38 -0
- package/dist/cloud/secret-store.js +103 -0
- package/dist/cloud/security.d.ts +26 -0
- package/dist/cloud/security.js +142 -0
- package/dist/cloud/server.d.ts +21 -0
- package/dist/cloud/server.js +1079 -0
- package/dist/cloud/shared-state.d.ts +72 -0
- package/dist/cloud/shared-state.js +159 -0
- package/dist/cloud/ssrf.d.ts +43 -0
- package/dist/cloud/ssrf.js +150 -0
- package/dist/cloud/store.d.ts +41 -0
- package/dist/cloud/store.js +75 -0
- package/dist/cloud/stripe.d.ts +78 -0
- package/dist/cloud/stripe.js +317 -0
- package/dist/cloud/telemetry-store.d.ts +53 -0
- package/dist/cloud/telemetry-store.js +108 -0
- package/dist/cloud/web/auth.d.ts +225 -0
- package/dist/cloud/web/auth.js +555 -0
- package/dist/cloud/web/charts.d.ts +70 -0
- package/dist/cloud/web/charts.js +178 -0
- package/dist/cloud/web/csrf.d.ts +14 -0
- package/dist/cloud/web/csrf.js +22 -0
- package/dist/cloud/web/docs.d.ts +40 -0
- package/dist/cloud/web/docs.js +174 -0
- package/dist/cloud/web/router.d.ts +25 -0
- package/dist/cloud/web/router.js +1921 -0
- package/dist/cloud/web/static/alpine.min.js +5 -0
- package/dist/cloud/web/static/favicon.svg +4 -0
- package/dist/cloud/web/static/htmx-sse.js +290 -0
- package/dist/cloud/web/static/htmx.min.js +1 -0
- package/dist/cloud/web/static/style.css +2683 -0
- package/dist/cloud/web/static-server.d.ts +13 -0
- package/dist/cloud/web/static-server.js +73 -0
- package/dist/cloud/web/template-engine.d.ts +27 -0
- package/dist/cloud/web/template-engine.js +146 -0
- package/dist/cloud/web/templates/layouts/admin.hbs +57 -0
- package/dist/cloud/web/templates/layouts/auth.hbs +138 -0
- package/dist/cloud/web/templates/layouts/base.hbs +16 -0
- package/dist/cloud/web/templates/layouts/dashboard.hbs +39 -0
- package/dist/cloud/web/templates/layouts/landing.hbs +82 -0
- package/dist/cloud/web/templates/pages/admin/overview.hbs +123 -0
- package/dist/cloud/web/templates/pages/admin/servers.hbs +129 -0
- package/dist/cloud/web/templates/pages/admin/telemetry.hbs +39 -0
- package/dist/cloud/web/templates/pages/admin/user-edit.hbs +91 -0
- package/dist/cloud/web/templates/pages/admin/users.hbs +179 -0
- package/dist/cloud/web/templates/pages/auth/forgot-password.hbs +25 -0
- package/dist/cloud/web/templates/pages/auth/login.hbs +33 -0
- package/dist/cloud/web/templates/pages/auth/register.hbs +32 -0
- package/dist/cloud/web/templates/pages/auth/reset-password.hbs +34 -0
- package/dist/cloud/web/templates/pages/dashboard/billing.hbs +140 -0
- package/dist/cloud/web/templates/pages/dashboard/create.hbs +173 -0
- package/dist/cloud/web/templates/pages/dashboard/index.hbs +8 -0
- package/dist/cloud/web/templates/pages/dashboard/server-detail.hbs +280 -0
- package/dist/cloud/web/templates/pages/dashboard/server-logs.hbs +35 -0
- package/dist/cloud/web/templates/pages/dashboard/server-metrics.hbs +63 -0
- package/dist/cloud/web/templates/pages/dashboard/servers-partial.hbs +21 -0
- package/dist/cloud/web/templates/pages/dashboard/servers.hbs +44 -0
- package/dist/cloud/web/templates/pages/docs/show.hbs +16 -0
- package/dist/cloud/web/templates/pages/errors/404.hbs +9 -0
- package/dist/cloud/web/templates/pages/errors/500.hbs +8 -0
- package/dist/cloud/web/templates/pages/landing/index.hbs +223 -0
- package/dist/cloud/web/templates/pages/legal/privacy.hbs +71 -0
- package/dist/cloud/web/templates/pages/legal/terms.hbs +73 -0
- package/dist/cloud/web/templates/partials/admin-stats.hbs +52 -0
- package/dist/cloud/web/templates/partials/flash-message.hbs +6 -0
- package/dist/cloud/web/templates/partials/pricing-table.hbs +103 -0
- package/dist/cloud/web/templates/partials/server-card.hbs +19 -0
- package/dist/cloud/web/templates/partials/status-badge.hbs +1 -0
- package/dist/commands/bundle.d.ts +18 -0
- package/dist/commands/bundle.js +82 -0
- package/dist/commands/ci.d.ts +25 -0
- package/dist/commands/ci.js +149 -0
- package/dist/commands/deploy.d.ts +24 -0
- package/dist/commands/deploy.js +145 -0
- package/dist/commands/diff.d.ts +18 -0
- package/dist/commands/diff.js +185 -0
- package/dist/commands/from/describe.d.ts +65 -0
- package/dist/commands/from/describe.js +173 -0
- package/dist/commands/from/har.d.ts +81 -0
- package/dist/commands/from/har.js +255 -0
- package/dist/commands/from/openapi.d.ts +105 -0
- package/dist/commands/from/openapi.js +302 -0
- package/dist/commands/from/postman.d.ts +51 -0
- package/dist/commands/from/postman.js +146 -0
- package/dist/commands/from/target-support.d.ts +11 -0
- package/dist/commands/from/target-support.js +33 -0
- package/dist/commands/from/url.d.ts +75 -0
- package/dist/commands/from/url.js +244 -0
- package/dist/commands/from/website.d.ts +75 -0
- package/dist/commands/from/website.js +284 -0
- package/dist/commands/lint.d.ts +24 -0
- package/dist/commands/lint.js +184 -0
- package/dist/commands/merge.d.ts +18 -0
- package/dist/commands/merge.js +161 -0
- package/dist/commands/publish.d.ts +27 -0
- package/dist/commands/publish.js +334 -0
- package/dist/commands/rescan.d.ts +40 -0
- package/dist/commands/rescan.js +255 -0
- package/dist/commands/update.d.ts +14 -0
- package/dist/commands/update.js +87 -0
- package/dist/commands/verify.d.ts +14 -0
- package/dist/commands/verify.js +71 -0
- package/dist/config/configurable-command.d.ts +13 -0
- package/dist/config/configurable-command.js +70 -0
- package/dist/config/mcpmake-config.d.ts +68 -0
- package/dist/config/mcpmake-config.js +207 -0
- package/dist/docs/cli.md +400 -0
- package/dist/docs/mcp-2026-07-28-migration.md +78 -0
- package/dist/docs/migrate-from-stainless.md +94 -0
- package/dist/docs/quickstart.md +166 -0
- package/dist/docs/show-hn.md +26 -0
- package/dist/docs/website-servers.md +169 -0
- package/dist/emitter/code-writer.d.ts +8 -0
- package/dist/emitter/code-writer.js +25 -0
- package/dist/emitter/index.d.ts +32 -0
- package/dist/emitter/index.js +280 -0
- package/dist/emitter/mcpb-bundler.d.ts +31 -0
- package/dist/emitter/mcpb-bundler.js +172 -0
- package/dist/emitter/project-scaffolder.d.ts +4 -0
- package/dist/emitter/project-scaffolder.js +89 -0
- package/dist/emitter/python-template-loader.d.ts +4 -0
- package/dist/emitter/python-template-loader.js +30 -0
- package/dist/emitter/python-templates/dockerfile.hbs +14 -0
- package/dist/emitter/python-templates/env.example.hbs +6 -0
- package/dist/emitter/python-templates/requirements.txt.hbs +4 -0
- package/dist/emitter/python-templates/server.py.hbs +77 -0
- package/dist/emitter/site-scaffolder.d.ts +13 -0
- package/dist/emitter/site-scaffolder.js +70 -0
- package/dist/emitter/site-template-loader.d.ts +5 -0
- package/dist/emitter/site-template-loader.js +47 -0
- package/dist/emitter/site-templates/browser-manager.ts.hbs +233 -0
- package/dist/emitter/site-templates/config.ts.hbs +28 -0
- package/dist/emitter/site-templates/dockerfile.hbs +31 -0
- package/dist/emitter/site-templates/env.example.hbs +19 -0
- package/dist/emitter/site-templates/package.json.hbs +26 -0
- package/dist/emitter/site-templates/server-main-http.ts.hbs +108 -0
- package/dist/emitter/site-templates/server-main.ts.hbs +23 -0
- package/dist/emitter/site-templates/tool-handler-action.ts.hbs +86 -0
- package/dist/emitter/site-templates/tool-handler-form.ts.hbs +116 -0
- package/dist/emitter/site-templates/tool-handler-lifecycle.ts.hbs +146 -0
- package/dist/emitter/site-templates/tool-index.ts.hbs +11 -0
- package/dist/emitter/template-loader.d.ts +1 -0
- package/dist/emitter/template-loader.js +27 -0
- package/dist/emitter/templates/auth-provider.ts.hbs +57 -0
- package/dist/emitter/templates/config.ts.hbs +63 -0
- package/dist/emitter/templates/discovery.ts.hbs +301 -0
- package/dist/emitter/templates/dockerfile.hbs +34 -0
- package/dist/emitter/templates/env.example.hbs +28 -0
- package/dist/emitter/templates/gitignore.hbs +5 -0
- package/dist/emitter/templates/http-executor.ts.hbs +117 -0
- package/dist/emitter/templates/oauth.ts.hbs +188 -0
- package/dist/emitter/templates/package.json.hbs +25 -0
- package/dist/emitter/templates/prompts.ts.hbs +22 -0
- package/dist/emitter/templates/readme.md.hbs +123 -0
- package/dist/emitter/templates/resources.ts.hbs +63 -0
- package/dist/emitter/templates/server-main-http.ts.hbs +407 -0
- package/dist/emitter/templates/server-main.ts.hbs +40 -0
- package/dist/emitter/templates/task-handlers.ts.hbs +189 -0
- package/dist/emitter/templates/task-manager.ts.hbs +139 -0
- package/dist/emitter/templates/task-sse.ts.hbs +105 -0
- package/dist/emitter/templates/tool-handler.ts.hbs +124 -0
- package/dist/emitter/templates/tool-index.ts.hbs +11 -0
- package/dist/emitter/templates/tool-test.ts.hbs +57 -0
- package/dist/emitter/templates/trace.ts.hbs +79 -0
- package/dist/emitter/templates/tsconfig.json.hbs +16 -0
- package/dist/emitter/templates/types.ts.hbs +5 -0
- package/dist/emitter/worker-template-loader.d.ts +5 -0
- package/dist/emitter/worker-template-loader.js +33 -0
- package/dist/emitter/worker-templates/config.ts.hbs +54 -0
- package/dist/emitter/worker-templates/dev-vars.example.hbs +10 -0
- package/dist/emitter/worker-templates/gitignore.hbs +6 -0
- package/dist/emitter/worker-templates/package.json.hbs +24 -0
- package/dist/emitter/worker-templates/readme.md.hbs +53 -0
- package/dist/emitter/worker-templates/server.test.ts.hbs +20 -0
- package/dist/emitter/worker-templates/tool-handler.ts.hbs +85 -0
- package/dist/emitter/worker-templates/tool-index.ts.hbs +28 -0
- package/dist/emitter/worker-templates/tsconfig.json.hbs +17 -0
- package/dist/emitter/worker-templates/worker.ts.hbs +242 -0
- package/dist/emitter/worker-templates/wrangler.toml.hbs +19 -0
- package/dist/generator/spec-generator.d.ts +6 -0
- package/dist/generator/spec-generator.js +50 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +64 -0
- package/dist/parser/har-filter.d.ts +8 -0
- package/dist/parser/har-filter.js +71 -0
- package/dist/parser/har-loader.d.ts +2 -0
- package/dist/parser/har-loader.js +14 -0
- package/dist/parser/har-normalizer.d.ts +20 -0
- package/dist/parser/har-normalizer.js +78 -0
- package/dist/parser/index.d.ts +10 -0
- package/dist/parser/index.js +6 -0
- package/dist/parser/openapi-loader.d.ts +6 -0
- package/dist/parser/openapi-loader.js +308 -0
- package/dist/parser/operation-extractor.d.ts +13 -0
- package/dist/parser/operation-extractor.js +155 -0
- package/dist/parser/overlay-loader.d.ts +10 -0
- package/dist/parser/overlay-loader.js +184 -0
- package/dist/parser/postman-loader.d.ts +9 -0
- package/dist/parser/postman-loader.js +106 -0
- package/dist/parser/schema-converter.d.ts +12 -0
- package/dist/parser/schema-converter.js +117 -0
- package/dist/plugins/adapter.d.ts +40 -0
- package/dist/plugins/adapter.js +15 -0
- package/dist/plugins/loader.d.ts +25 -0
- package/dist/plugins/loader.js +58 -0
- package/dist/pricing.d.ts +55 -0
- package/dist/pricing.js +133 -0
- package/dist/providers/index.d.ts +15 -0
- package/dist/providers/index.js +56 -0
- package/dist/recorder/browser-recorder.d.ts +22 -0
- package/dist/recorder/browser-recorder.js +205 -0
- package/dist/registry/official-registry.d.ts +90 -0
- package/dist/registry/official-registry.js +129 -0
- package/dist/rescan/diff-engine.d.ts +5 -0
- package/dist/rescan/diff-engine.js +312 -0
- package/dist/rescan/index.d.ts +3 -0
- package/dist/rescan/index.js +2 -0
- package/dist/rescan/rescan-runner.d.ts +42 -0
- package/dist/rescan/rescan-runner.js +69 -0
- package/dist/rescan/rescan-scheduler.d.ts +41 -0
- package/dist/rescan/rescan-scheduler.js +179 -0
- package/dist/site-transformer/browser-tools.d.ts +10 -0
- package/dist/site-transformer/browser-tools.js +59 -0
- package/dist/site-transformer/index.d.ts +2 -0
- package/dist/site-transformer/index.js +2 -0
- package/dist/site-transformer/selector-healer.d.ts +8 -0
- package/dist/site-transformer/selector-healer.js +106 -0
- package/dist/site-transformer/tool-generator.d.ts +13 -0
- package/dist/site-transformer/tool-generator.js +245 -0
- package/dist/transformer/auth-detector.d.ts +13 -0
- package/dist/transformer/auth-detector.js +90 -0
- package/dist/transformer/catalog-builder.d.ts +18 -0
- package/dist/transformer/catalog-builder.js +56 -0
- package/dist/transformer/client-compat.d.ts +6 -0
- package/dist/transformer/client-compat.js +44 -0
- package/dist/transformer/har-clusterer.d.ts +9 -0
- package/dist/transformer/har-clusterer.js +27 -0
- package/dist/transformer/har-dedup.d.ts +10 -0
- package/dist/transformer/har-dedup.js +81 -0
- package/dist/transformer/har-schema-inferrer.d.ts +15 -0
- package/dist/transformer/har-schema-inferrer.js +90 -0
- package/dist/transformer/har-to-operations.d.ts +13 -0
- package/dist/transformer/har-to-operations.js +192 -0
- package/dist/transformer/index.d.ts +8 -0
- package/dist/transformer/index.js +6 -0
- package/dist/transformer/llm-namer.d.ts +6 -0
- package/dist/transformer/llm-namer.js +59 -0
- package/dist/transformer/naming.d.ts +4 -0
- package/dist/transformer/naming.js +30 -0
- package/dist/transformer/operation-filter.d.ts +13 -0
- package/dist/transformer/operation-filter.js +52 -0
- package/dist/transformer/resource-builder.d.ts +12 -0
- package/dist/transformer/resource-builder.js +80 -0
- package/dist/transformer/schema-merger.d.ts +14 -0
- package/dist/transformer/schema-merger.js +65 -0
- package/dist/transformer/tool-builder.d.ts +3 -0
- package/dist/transformer/tool-builder.js +114 -0
- package/dist/types/index.d.ts +131 -0
- package/dist/types/index.js +1 -0
- package/dist/types/site.d.ts +284 -0
- package/dist/types/site.js +8 -0
- package/dist/utils/fail.d.ts +48 -0
- package/dist/utils/fail.js +204 -0
- package/dist/utils/fs.d.ts +5 -0
- package/dist/utils/fs.js +28 -0
- package/dist/utils/interactive.d.ts +6 -0
- package/dist/utils/interactive.js +30 -0
- package/dist/utils/logger.d.ts +1 -0
- package/dist/utils/logger.js +2 -0
- package/dist/utils/sanitize.d.ts +28 -0
- package/dist/utils/sanitize.js +44 -0
- package/dist/utils/watcher.d.ts +11 -0
- package/dist/utils/watcher.js +36 -0
- package/package.json +65 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
<div class="admin-page">
|
|
2
|
+
<div class="admin-page-header">
|
|
3
|
+
<h1>System Overview</h1>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<div id="admin-stats" hx-get="/admin/_stats" hx-trigger="every 10s" hx-swap="innerHTML" aria-live="polite">
|
|
7
|
+
{{> admin-stats}}
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<div class="health-panel">
|
|
11
|
+
<h2>Health</h2>
|
|
12
|
+
<div class="health-grid">
|
|
13
|
+
<div class="health-item">
|
|
14
|
+
<span class="health-badge {{#if health.db}}health-badge--ok{{else}}health-badge--bad{{/if}}">{{#if health.db}}OK{{else}}OFF{{/if}}</span>
|
|
15
|
+
<div class="health-meta">
|
|
16
|
+
<span class="health-name">Database</span>
|
|
17
|
+
<span class="health-sub">{{#if health.db}}reachable{{else}}in-memory mode{{/if}}</span>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="health-item">
|
|
21
|
+
<span class="health-badge {{#if health.stripe}}health-badge--ok{{else}}health-badge--bad{{/if}}">{{#if health.stripe}}OK{{else}}OFF{{/if}}</span>
|
|
22
|
+
<div class="health-meta">
|
|
23
|
+
<span class="health-name">Stripe</span>
|
|
24
|
+
<span class="health-sub">{{#if health.stripe}}configured{{else}}not configured{{/if}}</span>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="health-item">
|
|
28
|
+
<span class="health-badge health-badge--neutral">{{health.backend}}</span>
|
|
29
|
+
<div class="health-meta">
|
|
30
|
+
<span class="health-name">Container backend</span>
|
|
31
|
+
<span class="health-sub">driver</span>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="health-item">
|
|
35
|
+
<span class="health-badge {{#if health.ramOk}}health-badge--ok{{else}}health-badge--bad{{/if}}">{{#if health.ramOk}}OK{{else}}ALERT{{/if}}</span>
|
|
36
|
+
<div class="health-meta">
|
|
37
|
+
<span class="health-name">RAM pressure</span>
|
|
38
|
+
<span class="health-sub">{{health.memUsedPct}}% used</span>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="health-item">
|
|
42
|
+
<span class="health-badge {{#if health.diskOk}}health-badge--ok{{else}}health-badge--bad{{/if}}">{{#if health.diskOk}}OK{{else}}ALERT{{/if}}</span>
|
|
43
|
+
<div class="health-meta">
|
|
44
|
+
<span class="health-name">Disk pressure</span>
|
|
45
|
+
<span class="health-sub">{{health.diskUsedPct}}% used</span>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
{{#if health.alerts.length}}
|
|
50
|
+
<ul class="health-alerts">
|
|
51
|
+
{{#each health.alerts}}<li>{{this}}</li>{{/each}}
|
|
52
|
+
</ul>
|
|
53
|
+
{{/if}}
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div class="chart-panel">
|
|
57
|
+
<div class="chart-panel-header">
|
|
58
|
+
<h2>API failure rate</h2>
|
|
59
|
+
<span class="chart-panel-meta">last 60 minutes</span>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="failure-rate-grid">
|
|
62
|
+
<div class="stat-card">
|
|
63
|
+
<span class="stat-card-value">{{failure.requests}}</span>
|
|
64
|
+
<span class="stat-card-label">Requests (last hour)</span>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="stat-card">
|
|
67
|
+
<span class="stat-card-value {{#if (eq failure.serverErrorRatePct '0.0')}}status-green{{else}}status-red{{/if}}">{{failure.serverErrorRatePct}}%</span>
|
|
68
|
+
<span class="stat-card-label">Server error rate (5xx)</span>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="stat-card">
|
|
71
|
+
<span class="stat-card-value">{{failure.clientErrorRatePct}}%</span>
|
|
72
|
+
<span class="stat-card-label">Client error rate (4xx)</span>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="chart-box">
|
|
76
|
+
<span class="chart-box-label">Requests / minute (live)</span>
|
|
77
|
+
{{{lastHourChart}}}
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div class="chart-panel">
|
|
82
|
+
<div class="chart-panel-header">
|
|
83
|
+
<h2>History</h2>
|
|
84
|
+
<span class="chart-panel-meta">last 24 hours</span>
|
|
85
|
+
</div>
|
|
86
|
+
{{#if hasHistory}}
|
|
87
|
+
<div class="chart-grid">
|
|
88
|
+
<div class="chart-box">
|
|
89
|
+
<span class="chart-box-label">Requests vs 5xx errors</span>
|
|
90
|
+
{{{charts.requestsErrors}}}
|
|
91
|
+
<div class="chart-legend">
|
|
92
|
+
<span class="chart-legend-item"><span class="chart-swatch chart-swatch--a"></span>Requests</span>
|
|
93
|
+
<span class="chart-legend-item"><span class="chart-swatch chart-swatch--b"></span>5xx errors</span>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="chart-box">
|
|
97
|
+
<span class="chart-box-label">Memory vs disk used (%)</span>
|
|
98
|
+
{{{charts.memDisk}}}
|
|
99
|
+
<div class="chart-legend">
|
|
100
|
+
<span class="chart-legend-item"><span class="chart-swatch chart-swatch--a"></span>Memory</span>
|
|
101
|
+
<span class="chart-legend-item"><span class="chart-swatch chart-swatch--b"></span>Disk</span>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
<div class="chart-box">
|
|
105
|
+
<span class="chart-box-label">Total users</span>
|
|
106
|
+
{{{charts.users}}}
|
|
107
|
+
</div>
|
|
108
|
+
<div class="chart-box">
|
|
109
|
+
<span class="chart-box-label">Total servers</span>
|
|
110
|
+
{{{charts.servers}}}
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
{{else}}
|
|
114
|
+
<div class="chart-empty">
|
|
115
|
+
{{#if historyEmpty}}
|
|
116
|
+
<p>History requires <code>DATABASE_URL</code>. Configure a database to retain metric samples across restarts.</p>
|
|
117
|
+
{{else}}
|
|
118
|
+
<p>No metric samples recorded yet. Charts will populate as the sampler runs.</p>
|
|
119
|
+
{{/if}}
|
|
120
|
+
</div>
|
|
121
|
+
{{/if}}
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<div class="admin-page" x-data="adminServers()">
|
|
2
|
+
<div class="admin-page-header">
|
|
3
|
+
<h1>All Servers</h1>
|
|
4
|
+
<span class="admin-page-count">{{servers.length}} total</span>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<input
|
|
8
|
+
type="text"
|
|
9
|
+
class="search-input"
|
|
10
|
+
placeholder="Filter by slug, user, or status..."
|
|
11
|
+
x-model="search"
|
|
12
|
+
>
|
|
13
|
+
|
|
14
|
+
<div class="admin-table-wrap">
|
|
15
|
+
<table class="admin-table">
|
|
16
|
+
<thead>
|
|
17
|
+
<tr>
|
|
18
|
+
<th @click="sortBy('slug')" :class="sortClass('slug')">Slug</th>
|
|
19
|
+
<th @click="sortBy('status')" :class="sortClass('status')">Status</th>
|
|
20
|
+
<th @click="sortBy('userId')" :class="sortClass('userId')">User</th>
|
|
21
|
+
<th @click="sortBy('toolCount')" :class="sortClass('toolCount')">Tools</th>
|
|
22
|
+
<th @click="sortBy('port')" :class="sortClass('port')">Port</th>
|
|
23
|
+
<th @click="sortBy('createdAt')" :class="sortClass('createdAt')">Created</th>
|
|
24
|
+
<th @click="sortBy('lastActiveAt')" :class="sortClass('lastActiveAt')">Last Active</th>
|
|
25
|
+
<th>Actions</th>
|
|
26
|
+
</tr>
|
|
27
|
+
</thead>
|
|
28
|
+
<tbody>
|
|
29
|
+
<template x-for="server in filteredServers()" :key="server.slug">
|
|
30
|
+
<tr>
|
|
31
|
+
<td><code x-text="server.slug"></code></td>
|
|
32
|
+
<td>
|
|
33
|
+
<span
|
|
34
|
+
class="status-dot"
|
|
35
|
+
:class="{
|
|
36
|
+
'status-green': server.status === 'running',
|
|
37
|
+
'status-gray': server.status === 'stopped',
|
|
38
|
+
'status-red': server.status === 'error',
|
|
39
|
+
'status-yellow': server.status === 'building'
|
|
40
|
+
}"
|
|
41
|
+
x-text="server.status"
|
|
42
|
+
></span>
|
|
43
|
+
</td>
|
|
44
|
+
<td x-text="server.userEmail || server.userId || '(anonymous)'"></td>
|
|
45
|
+
<td x-text="server.toolCount"></td>
|
|
46
|
+
<td><code x-text="server.port"></code></td>
|
|
47
|
+
<td x-text="formatDate(server.createdAt)"></td>
|
|
48
|
+
<td x-text="formatDate(server.lastActiveAt)"></td>
|
|
49
|
+
<td class="admin-actions">
|
|
50
|
+
<form method="POST" :action="'/admin/servers/' + server.slug + '/delete'" style="display:inline">
|
|
51
|
+
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
|
52
|
+
<button
|
|
53
|
+
type="submit"
|
|
54
|
+
class="btn btn-sm btn-danger"
|
|
55
|
+
@click.prevent="confirmDelete($event, server.slug)"
|
|
56
|
+
>Delete</button>
|
|
57
|
+
</form>
|
|
58
|
+
</td>
|
|
59
|
+
</tr>
|
|
60
|
+
</template>
|
|
61
|
+
<template x-if="filteredServers().length === 0">
|
|
62
|
+
<tr>
|
|
63
|
+
<td colspan="8" class="admin-table-empty">No servers match your filter.</td>
|
|
64
|
+
</tr>
|
|
65
|
+
</template>
|
|
66
|
+
</tbody>
|
|
67
|
+
</table>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<script>
|
|
72
|
+
function adminServers() {
|
|
73
|
+
return {
|
|
74
|
+
search: '',
|
|
75
|
+
sortCol: 'createdAt',
|
|
76
|
+
sortDir: 'desc',
|
|
77
|
+
servers: {{{json servers}}},
|
|
78
|
+
|
|
79
|
+
sortBy(col) {
|
|
80
|
+
if (this.sortCol === col) {
|
|
81
|
+
this.sortDir = this.sortDir === 'asc' ? 'desc' : 'asc';
|
|
82
|
+
} else {
|
|
83
|
+
this.sortCol = col;
|
|
84
|
+
this.sortDir = 'asc';
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
sortClass(col) {
|
|
89
|
+
if (this.sortCol !== col) return 'sortable';
|
|
90
|
+
return 'sortable sorted-' + this.sortDir;
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
filteredServers() {
|
|
94
|
+
let result = this.servers;
|
|
95
|
+
const q = this.search.toLowerCase().trim();
|
|
96
|
+
if (q) {
|
|
97
|
+
result = result.filter(s =>
|
|
98
|
+
s.slug.toLowerCase().includes(q) ||
|
|
99
|
+
(s.userEmail || '').toLowerCase().includes(q) ||
|
|
100
|
+
(s.userId || '').toLowerCase().includes(q) ||
|
|
101
|
+
s.status.toLowerCase().includes(q)
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
const col = this.sortCol;
|
|
105
|
+
const dir = this.sortDir === 'asc' ? 1 : -1;
|
|
106
|
+
result = [...result].sort((a, b) => {
|
|
107
|
+
const va = a[col] ?? '';
|
|
108
|
+
const vb = b[col] ?? '';
|
|
109
|
+
if (typeof va === 'number' && typeof vb === 'number') return (va - vb) * dir;
|
|
110
|
+
return String(va).localeCompare(String(vb)) * dir;
|
|
111
|
+
});
|
|
112
|
+
return result;
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
formatDate(iso) {
|
|
116
|
+
if (!iso) return '';
|
|
117
|
+
const d = new Date(iso);
|
|
118
|
+
if (isNaN(d.getTime())) return iso;
|
|
119
|
+
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
confirmDelete(event, slug) {
|
|
123
|
+
if (confirm('Delete server "' + slug + '"? This will stop the container and remove all data.')) {
|
|
124
|
+
event.target.closest('form').submit();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
</script>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<div class="admin-page">
|
|
2
|
+
<div class="admin-page-header">
|
|
3
|
+
<h1>Telemetry</h1>
|
|
4
|
+
<span class="admin-page-count">{{groups.length}} groups</span>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<p class="form-hint">CLI crash and error reports, grouped by fingerprint. PII is stripped at ingest.</p>
|
|
8
|
+
|
|
9
|
+
<div class="admin-table-wrap">
|
|
10
|
+
<table class="admin-table">
|
|
11
|
+
<thead>
|
|
12
|
+
<tr>
|
|
13
|
+
<th>Count</th>
|
|
14
|
+
<th>Last seen</th>
|
|
15
|
+
<th>Command</th>
|
|
16
|
+
<th>Error</th>
|
|
17
|
+
<th>CLI</th>
|
|
18
|
+
<th>Platform</th>
|
|
19
|
+
</tr>
|
|
20
|
+
</thead>
|
|
21
|
+
<tbody>
|
|
22
|
+
{{#each groups}}
|
|
23
|
+
<tr>
|
|
24
|
+
<td><span class="telemetry-count">{{this.count}}</span></td>
|
|
25
|
+
<td>{{formatDate this.lastSeen}}</td>
|
|
26
|
+
<td><code>{{this.command}}</code></td>
|
|
27
|
+
<td class="telemetry-error" title="{{this.errorMessage}}">{{truncate this.errorMessage 120}}</td>
|
|
28
|
+
<td><code>{{this.cliVersion}}</code></td>
|
|
29
|
+
<td>{{this.platform}}</td>
|
|
30
|
+
</tr>
|
|
31
|
+
{{else}}
|
|
32
|
+
<tr>
|
|
33
|
+
<td colspan="6" class="admin-table-empty">No telemetry reports yet.</td>
|
|
34
|
+
</tr>
|
|
35
|
+
{{/each}}
|
|
36
|
+
</tbody>
|
|
37
|
+
</table>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<div class="admin-page">
|
|
2
|
+
<div class="admin-page-header">
|
|
3
|
+
<a href="/admin/users" class="admin-back-link">← Users</a>
|
|
4
|
+
<h1>{{targetUser.email}}</h1>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<div class="admin-stats">
|
|
8
|
+
<div class="stat-card">
|
|
9
|
+
<span class="stat-card-value">
|
|
10
|
+
<span class="plan-badge plan-badge--{{targetUser.plan}}">{{targetUser.plan}}</span>
|
|
11
|
+
</span>
|
|
12
|
+
<span class="stat-card-label">Plan</span>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="stat-card">
|
|
15
|
+
<span class="stat-card-value">{{balance}}</span>
|
|
16
|
+
<span class="stat-card-label">Credit Balance</span>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="stat-card">
|
|
19
|
+
<span class="stat-card-value">{{serverCount}}</span>
|
|
20
|
+
<span class="stat-card-label">Servers</span>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="stat-card">
|
|
23
|
+
<span class="stat-card-value {{#if targetUser.isAdmin}}status-green{{/if}}">{{#if targetUser.isAdmin}}Yes{{else}}No{{/if}}</span>
|
|
24
|
+
<span class="stat-card-label">Admin</span>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div class="system-info">
|
|
29
|
+
<h2>Account</h2>
|
|
30
|
+
<dl class="system-info-list">
|
|
31
|
+
<div class="system-info-row">
|
|
32
|
+
<dt>Email</dt>
|
|
33
|
+
<dd>{{targetUser.email}}</dd>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="system-info-row">
|
|
36
|
+
<dt>Registered</dt>
|
|
37
|
+
<dd>{{formatDate targetUser.createdAt}}</dd>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="system-info-row">
|
|
40
|
+
<dt>Last login</dt>
|
|
41
|
+
<dd>{{#if targetUser.lastLoginAt}}{{formatDate targetUser.lastLoginAt}}{{else}}—{{/if}}</dd>
|
|
42
|
+
</div>
|
|
43
|
+
</dl>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div class="credit-panel">
|
|
47
|
+
<h2>Adjust credits</h2>
|
|
48
|
+
<p class="form-hint">Grant or revoke promotional tool-call credits. Recorded in the ledger below.</p>
|
|
49
|
+
<form method="POST" action="/admin/users/{{targetUser.id}}/credits" class="credit-form">
|
|
50
|
+
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
|
51
|
+
<div class="credit-form-row">
|
|
52
|
+
<label class="credit-form-field">
|
|
53
|
+
<span>Amount</span>
|
|
54
|
+
<input type="number" name="amount" min="1" max="100000000" step="1" required class="search-input" placeholder="e.g. 1000">
|
|
55
|
+
</label>
|
|
56
|
+
<div class="credit-form-actions">
|
|
57
|
+
<button type="submit" name="action" value="grant" class="btn btn-primary">Grant</button>
|
|
58
|
+
<button type="submit" name="action" value="revoke" class="btn btn-secondary">Revoke</button>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</form>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div class="admin-table-wrap">
|
|
65
|
+
<h2 class="ledger-heading">Recent ledger</h2>
|
|
66
|
+
<table class="admin-table">
|
|
67
|
+
<thead>
|
|
68
|
+
<tr>
|
|
69
|
+
<th>When</th>
|
|
70
|
+
<th>Change</th>
|
|
71
|
+
<th>Reason</th>
|
|
72
|
+
<th>Balance after</th>
|
|
73
|
+
</tr>
|
|
74
|
+
</thead>
|
|
75
|
+
<tbody>
|
|
76
|
+
{{#each ledger}}
|
|
77
|
+
<tr>
|
|
78
|
+
<td>{{formatDate this.createdAt}}</td>
|
|
79
|
+
<td><span class="{{#if this.isCredit}}status-green{{else}}status-red{{/if}}">{{this.deltaLabel}}</span></td>
|
|
80
|
+
<td><code>{{this.reason}}</code></td>
|
|
81
|
+
<td>{{this.balanceAfter}}</td>
|
|
82
|
+
</tr>
|
|
83
|
+
{{else}}
|
|
84
|
+
<tr>
|
|
85
|
+
<td colspan="4" class="admin-table-empty">No credit activity yet.</td>
|
|
86
|
+
</tr>
|
|
87
|
+
{{/each}}
|
|
88
|
+
</tbody>
|
|
89
|
+
</table>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
<div class="admin-page" x-data="adminUsers()">
|
|
2
|
+
<div class="admin-page-header">
|
|
3
|
+
<h1>User Management</h1>
|
|
4
|
+
<span class="admin-page-count">{{users.length}} total</span>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<input
|
|
8
|
+
type="text"
|
|
9
|
+
class="search-input"
|
|
10
|
+
placeholder="Filter by email or plan..."
|
|
11
|
+
x-model="search"
|
|
12
|
+
>
|
|
13
|
+
|
|
14
|
+
<div class="admin-table-wrap">
|
|
15
|
+
<table class="admin-table">
|
|
16
|
+
<thead>
|
|
17
|
+
<tr>
|
|
18
|
+
<th @click="sortBy('email')" :class="sortClass('email')">Email</th>
|
|
19
|
+
<th @click="sortBy('plan')" :class="sortClass('plan')">Plan</th>
|
|
20
|
+
<th @click="sortBy('isAdmin')" :class="sortClass('isAdmin')">Admin?</th>
|
|
21
|
+
<th @click="sortBy('serverCount')" :class="sortClass('serverCount')">Servers</th>
|
|
22
|
+
<th @click="sortBy('createdAt')" :class="sortClass('createdAt')">Registered</th>
|
|
23
|
+
<th @click="sortBy('lastLoginAt')" :class="sortClass('lastLoginAt')">Last Login</th>
|
|
24
|
+
<th>Actions</th>
|
|
25
|
+
</tr>
|
|
26
|
+
</thead>
|
|
27
|
+
<tbody>
|
|
28
|
+
<template x-for="user in filteredUsers()" :key="user.id">
|
|
29
|
+
<tr>
|
|
30
|
+
<td><a class="admin-row-link" :href="'/admin/users/' + user.id" x-text="user.email"></a></td>
|
|
31
|
+
<td>
|
|
32
|
+
<span
|
|
33
|
+
class="plan-badge"
|
|
34
|
+
:class="'plan-badge--' + user.plan"
|
|
35
|
+
x-text="user.plan"
|
|
36
|
+
></span>
|
|
37
|
+
</td>
|
|
38
|
+
<td>
|
|
39
|
+
<span
|
|
40
|
+
x-show="user.isAdmin"
|
|
41
|
+
class="admin-badge"
|
|
42
|
+
>admin</span>
|
|
43
|
+
</td>
|
|
44
|
+
<td x-text="user.serverCount"></td>
|
|
45
|
+
<td x-text="formatDate(user.createdAt)"></td>
|
|
46
|
+
<td x-text="formatDate(user.lastLoginAt)"></td>
|
|
47
|
+
<td class="admin-actions">
|
|
48
|
+
<form
|
|
49
|
+
class="inline-action"
|
|
50
|
+
method="POST"
|
|
51
|
+
:action="'/admin/users/' + user.id + '/plan'"
|
|
52
|
+
hx-post
|
|
53
|
+
:hx-post="'/admin/users/' + user.id + '/plan'"
|
|
54
|
+
hx-swap="none"
|
|
55
|
+
@htmx:after-request="handlePlanChange($event, user)"
|
|
56
|
+
>
|
|
57
|
+
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
|
58
|
+
<select name="plan" :value="user.plan" class="admin-select" @change="$el.closest('form').requestSubmit()">
|
|
59
|
+
<option value="free" :selected="user.plan === 'free'">free</option>
|
|
60
|
+
<option value="hobbyist" :selected="user.plan === 'hobbyist'">hobbyist</option>
|
|
61
|
+
<option value="pro" :selected="user.plan === 'pro'">pro</option>
|
|
62
|
+
<option value="team" :selected="user.plan === 'team'">team</option>
|
|
63
|
+
<option value="enterprise" :selected="user.plan === 'enterprise'">enterprise</option>
|
|
64
|
+
</select>
|
|
65
|
+
</form>
|
|
66
|
+
|
|
67
|
+
<form
|
|
68
|
+
class="inline-action"
|
|
69
|
+
method="POST"
|
|
70
|
+
:action="'/admin/users/' + user.id + '/admin'"
|
|
71
|
+
hx-post
|
|
72
|
+
:hx-post="'/admin/users/' + user.id + '/admin'"
|
|
73
|
+
hx-swap="none"
|
|
74
|
+
@htmx:after-request="handleAdminToggle($event, user)"
|
|
75
|
+
>
|
|
76
|
+
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
|
77
|
+
<button type="submit" class="btn btn-sm btn-secondary" x-text="user.isAdmin ? 'Revoke admin' : 'Make admin'"></button>
|
|
78
|
+
</form>
|
|
79
|
+
|
|
80
|
+
<form
|
|
81
|
+
class="inline-action"
|
|
82
|
+
method="POST"
|
|
83
|
+
:action="'/admin/users/' + user.id + '/delete'"
|
|
84
|
+
>
|
|
85
|
+
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
|
86
|
+
<button
|
|
87
|
+
type="submit"
|
|
88
|
+
class="btn btn-sm btn-danger"
|
|
89
|
+
@click.prevent="confirmDelete($event, user)"
|
|
90
|
+
>Delete</button>
|
|
91
|
+
</form>
|
|
92
|
+
</td>
|
|
93
|
+
</tr>
|
|
94
|
+
</template>
|
|
95
|
+
<template x-if="filteredUsers().length === 0">
|
|
96
|
+
<tr>
|
|
97
|
+
<td colspan="7" class="admin-table-empty">No users match your filter.</td>
|
|
98
|
+
</tr>
|
|
99
|
+
</template>
|
|
100
|
+
</tbody>
|
|
101
|
+
</table>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<script>
|
|
106
|
+
function adminUsers() {
|
|
107
|
+
return {
|
|
108
|
+
search: '',
|
|
109
|
+
sortCol: 'createdAt',
|
|
110
|
+
sortDir: 'desc',
|
|
111
|
+
users: {{{json users}}},
|
|
112
|
+
|
|
113
|
+
sortBy(col) {
|
|
114
|
+
if (this.sortCol === col) {
|
|
115
|
+
this.sortDir = this.sortDir === 'asc' ? 'desc' : 'asc';
|
|
116
|
+
} else {
|
|
117
|
+
this.sortCol = col;
|
|
118
|
+
this.sortDir = 'asc';
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
sortClass(col) {
|
|
123
|
+
if (this.sortCol !== col) return 'sortable';
|
|
124
|
+
return 'sortable sorted-' + this.sortDir;
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
filteredUsers() {
|
|
128
|
+
let result = this.users;
|
|
129
|
+
const q = this.search.toLowerCase().trim();
|
|
130
|
+
if (q) {
|
|
131
|
+
result = result.filter(u =>
|
|
132
|
+
u.email.toLowerCase().includes(q) ||
|
|
133
|
+
u.plan.toLowerCase().includes(q)
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
const col = this.sortCol;
|
|
137
|
+
const dir = this.sortDir === 'asc' ? 1 : -1;
|
|
138
|
+
result = [...result].sort((a, b) => {
|
|
139
|
+
const va = a[col] ?? '';
|
|
140
|
+
const vb = b[col] ?? '';
|
|
141
|
+
if (typeof va === 'number' && typeof vb === 'number') return (va - vb) * dir;
|
|
142
|
+
if (typeof va === 'boolean' && typeof vb === 'boolean') return ((va ? 1 : 0) - (vb ? 1 : 0)) * dir;
|
|
143
|
+
return String(va).localeCompare(String(vb)) * dir;
|
|
144
|
+
});
|
|
145
|
+
return result;
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
formatDate(iso) {
|
|
149
|
+
if (!iso) return '';
|
|
150
|
+
const d = new Date(iso);
|
|
151
|
+
if (isNaN(d.getTime())) return iso;
|
|
152
|
+
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
handlePlanChange(event, user) {
|
|
156
|
+
if (event.detail.successful) {
|
|
157
|
+
const form = event.target;
|
|
158
|
+
const select = form.querySelector('select[name="plan"]');
|
|
159
|
+
user.plan = select.value;
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
handleAdminToggle(event, user) {
|
|
164
|
+
if (event.detail.successful) {
|
|
165
|
+
user.isAdmin = !user.isAdmin;
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
confirmDelete(event, user) {
|
|
170
|
+
if (user.isAdmin) {
|
|
171
|
+
if (!confirm('This user is an admin. Are you sure you want to delete "' + user.email + '"?')) return;
|
|
172
|
+
} else {
|
|
173
|
+
if (!confirm('Delete user "' + user.email + '"? This cannot be undone.')) return;
|
|
174
|
+
}
|
|
175
|
+
event.target.closest('form').submit();
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
</script>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<h1>Reset password</h1>
|
|
2
|
+
<p class="auth-subtitle">Enter your email to receive a reset link</p>
|
|
3
|
+
|
|
4
|
+
{{#if error}}
|
|
5
|
+
<div class="auth-error" role="alert" aria-live="assertive">{{error}}</div>
|
|
6
|
+
{{/if}}
|
|
7
|
+
|
|
8
|
+
{{#if success}}
|
|
9
|
+
<div class="auth-success" role="status" aria-live="polite">{{success}}</div>
|
|
10
|
+
{{else}}
|
|
11
|
+
<form method="POST" action="/forgot-password">
|
|
12
|
+
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
|
13
|
+
|
|
14
|
+
<div class="form-group">
|
|
15
|
+
<label for="email">Email</label>
|
|
16
|
+
<input type="email" id="email" name="email" value="{{email}}" required autocomplete="email" autofocus>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<button type="submit" class="auth-submit">Send reset link</button>
|
|
20
|
+
</form>
|
|
21
|
+
{{/if}}
|
|
22
|
+
|
|
23
|
+
<div class="auth-footer">
|
|
24
|
+
<a href="/login">Back to sign in</a>
|
|
25
|
+
</div>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<h1>Sign in</h1>
|
|
2
|
+
<p class="auth-subtitle">Welcome back to mcpmake</p>
|
|
3
|
+
|
|
4
|
+
{{#if success}}
|
|
5
|
+
<div class="auth-success" role="status" aria-live="polite">{{success}}</div>
|
|
6
|
+
{{/if}}
|
|
7
|
+
|
|
8
|
+
{{#if error}}
|
|
9
|
+
<div class="auth-error" role="alert" aria-live="assertive">{{error}}</div>
|
|
10
|
+
{{/if}}
|
|
11
|
+
|
|
12
|
+
<form method="POST" action="/login">
|
|
13
|
+
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
|
14
|
+
|
|
15
|
+
<div class="form-group">
|
|
16
|
+
<label for="email">Email</label>
|
|
17
|
+
<input type="email" id="email" name="email" value="{{email}}" required autocomplete="email" autofocus>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="form-group">
|
|
21
|
+
<label for="password">Password</label>
|
|
22
|
+
<input type="password" id="password" name="password" required autocomplete="current-password">
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<button type="submit" class="auth-submit">Sign in</button>
|
|
26
|
+
</form>
|
|
27
|
+
|
|
28
|
+
<div class="auth-footer">
|
|
29
|
+
<a href="/forgot-password">Forgot your password?</a>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="auth-footer">
|
|
32
|
+
Don't have an account? <a href="/register">Register</a>
|
|
33
|
+
</div>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<h1>Create account</h1>
|
|
2
|
+
<p class="auth-subtitle">Get started with mcpmake Cloud</p>
|
|
3
|
+
|
|
4
|
+
{{#if error}}
|
|
5
|
+
<div class="auth-error" role="alert" aria-live="assertive">{{error}}</div>
|
|
6
|
+
{{/if}}
|
|
7
|
+
|
|
8
|
+
<form method="POST" action="/register">
|
|
9
|
+
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
|
10
|
+
|
|
11
|
+
<div class="form-group">
|
|
12
|
+
<label for="email">Email</label>
|
|
13
|
+
<input type="email" id="email" name="email" value="{{email}}" required autocomplete="email" autofocus>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="form-group">
|
|
17
|
+
<label for="password">Password</label>
|
|
18
|
+
<input type="password" id="password" name="password" required autocomplete="new-password" minlength="8">
|
|
19
|
+
<p class="form-hint">Minimum 8 characters</p>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div class="form-group">
|
|
23
|
+
<label for="confirmPassword">Confirm password</label>
|
|
24
|
+
<input type="password" id="confirmPassword" name="confirmPassword" required autocomplete="new-password">
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<button type="submit" class="auth-submit">Create account</button>
|
|
28
|
+
</form>
|
|
29
|
+
|
|
30
|
+
<div class="auth-footer">
|
|
31
|
+
Already have an account? <a href="/login">Sign in</a>
|
|
32
|
+
</div>
|