@betterportal/tunnels 1.0.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.
Files changed (116) hide show
  1. package/README.md +76 -0
  2. package/SPEC.md +524 -0
  3. package/lib/.bsb/clients/service-tunnel-client-api.d.ts +133 -0
  4. package/lib/.bsb/clients/service-tunnel-client-api.d.ts.map +1 -0
  5. package/lib/.bsb/clients/service-tunnel-client-api.js +102 -0
  6. package/lib/.bsb/clients/service-tunnel-client-api.js.map +1 -0
  7. package/lib/.bsb/clients/service-tunnel-web.d.ts +43 -0
  8. package/lib/.bsb/clients/service-tunnel-web.d.ts.map +1 -0
  9. package/lib/.bsb/clients/service-tunnel-web.js +47 -0
  10. package/lib/.bsb/clients/service-tunnel-web.js.map +1 -0
  11. package/lib/auth.d.ts +12 -0
  12. package/lib/auth.d.ts.map +1 -0
  13. package/lib/auth.js +59 -0
  14. package/lib/auth.js.map +1 -0
  15. package/lib/cli.d.ts +2 -0
  16. package/lib/cli.d.ts.map +1 -0
  17. package/lib/cli.js +295 -0
  18. package/lib/cli.js.map +1 -0
  19. package/lib/plugins/service-betterportal/.bp-generated/registry.d.ts +3 -0
  20. package/lib/plugins/service-betterportal/.bp-generated/registry.d.ts.map +1 -0
  21. package/lib/plugins/service-betterportal/.bp-generated/registry.js +127 -0
  22. package/lib/plugins/service-betterportal/.bp-generated/registry.js.map +1 -0
  23. package/lib/plugins/service-betterportal/assets/brand/bettertunnels-horizontal-light.svg +18 -0
  24. package/lib/plugins/service-betterportal/assets/brand/bettertunnels-square-dark-1024x1024.png +0 -0
  25. package/lib/plugins/service-betterportal/assets/brand/favicon.svg +12 -0
  26. package/lib/plugins/service-betterportal/betterportal-logo.png +0 -0
  27. package/lib/plugins/service-betterportal/bp-routes/cli-auth/verify/_theme.bootstrap1/index.d.ts +5 -0
  28. package/lib/plugins/service-betterportal/bp-routes/cli-auth/verify/_theme.bootstrap1/index.d.ts.map +1 -0
  29. package/lib/plugins/service-betterportal/bp-routes/cli-auth/verify/_theme.bootstrap1/index.js +6 -0
  30. package/lib/plugins/service-betterportal/bp-routes/cli-auth/verify/_theme.bootstrap1/index.js.map +1 -0
  31. package/lib/plugins/service-betterportal/bp-routes/cli-auth/verify/index.d.ts +25 -0
  32. package/lib/plugins/service-betterportal/bp-routes/cli-auth/verify/index.d.ts.map +1 -0
  33. package/lib/plugins/service-betterportal/bp-routes/cli-auth/verify/index.js +59 -0
  34. package/lib/plugins/service-betterportal/bp-routes/cli-auth/verify/index.js.map +1 -0
  35. package/lib/plugins/service-betterportal/bp-routes/dashboard/_theme.bootstrap1/index.d.ts +5 -0
  36. package/lib/plugins/service-betterportal/bp-routes/dashboard/_theme.bootstrap1/index.d.ts.map +1 -0
  37. package/lib/plugins/service-betterportal/bp-routes/dashboard/_theme.bootstrap1/index.js +22 -0
  38. package/lib/plugins/service-betterportal/bp-routes/dashboard/_theme.bootstrap1/index.js.map +1 -0
  39. package/lib/plugins/service-betterportal/bp-routes/dashboard/index.d.ts +40 -0
  40. package/lib/plugins/service-betterportal/bp-routes/dashboard/index.d.ts.map +1 -0
  41. package/lib/plugins/service-betterportal/bp-routes/dashboard/index.js +57 -0
  42. package/lib/plugins/service-betterportal/bp-routes/dashboard/index.js.map +1 -0
  43. package/lib/plugins/service-betterportal/bp-routes/downloads/_theme.bootstrap1/index.d.ts +5 -0
  44. package/lib/plugins/service-betterportal/bp-routes/downloads/_theme.bootstrap1/index.d.ts.map +1 -0
  45. package/lib/plugins/service-betterportal/bp-routes/downloads/_theme.bootstrap1/index.js +5 -0
  46. package/lib/plugins/service-betterportal/bp-routes/downloads/_theme.bootstrap1/index.js.map +1 -0
  47. package/lib/plugins/service-betterportal/bp-routes/downloads/index.d.ts +30 -0
  48. package/lib/plugins/service-betterportal/bp-routes/downloads/index.d.ts.map +1 -0
  49. package/lib/plugins/service-betterportal/bp-routes/downloads/index.js +45 -0
  50. package/lib/plugins/service-betterportal/bp-routes/downloads/index.js.map +1 -0
  51. package/lib/plugins/service-betterportal/bp-routes/landing/_theme.bootstrap1/index.d.ts +5 -0
  52. package/lib/plugins/service-betterportal/bp-routes/landing/_theme.bootstrap1/index.d.ts.map +1 -0
  53. package/lib/plugins/service-betterportal/bp-routes/landing/_theme.bootstrap1/index.js +5 -0
  54. package/lib/plugins/service-betterportal/bp-routes/landing/_theme.bootstrap1/index.js.map +1 -0
  55. package/lib/plugins/service-betterportal/bp-routes/landing/index.d.ts +40 -0
  56. package/lib/plugins/service-betterportal/bp-routes/landing/index.d.ts.map +1 -0
  57. package/lib/plugins/service-betterportal/bp-routes/landing/index.js +63 -0
  58. package/lib/plugins/service-betterportal/bp-routes/landing/index.js.map +1 -0
  59. package/lib/plugins/service-betterportal/bp-routes/tunnels/_theme.bootstrap1/index.d.ts +5 -0
  60. package/lib/plugins/service-betterportal/bp-routes/tunnels/_theme.bootstrap1/index.d.ts.map +1 -0
  61. package/lib/plugins/service-betterportal/bp-routes/tunnels/_theme.bootstrap1/index.js +5 -0
  62. package/lib/plugins/service-betterportal/bp-routes/tunnels/_theme.bootstrap1/index.js.map +1 -0
  63. package/lib/plugins/service-betterportal/bp-routes/tunnels/index.d.ts +44 -0
  64. package/lib/plugins/service-betterportal/bp-routes/tunnels/index.d.ts.map +1 -0
  65. package/lib/plugins/service-betterportal/bp-routes/tunnels/index.js +59 -0
  66. package/lib/plugins/service-betterportal/bp-routes/tunnels/index.js.map +1 -0
  67. package/lib/plugins/service-betterportal/brand-assets.d.ts +7 -0
  68. package/lib/plugins/service-betterportal/brand-assets.d.ts.map +1 -0
  69. package/lib/plugins/service-betterportal/brand-assets.js +24 -0
  70. package/lib/plugins/service-betterportal/brand-assets.js.map +1 -0
  71. package/lib/plugins/service-betterportal/index.d.ts +57 -0
  72. package/lib/plugins/service-betterportal/index.d.ts.map +1 -0
  73. package/lib/plugins/service-betterportal/index.js +108 -0
  74. package/lib/plugins/service-betterportal/index.js.map +1 -0
  75. package/lib/plugins/service-tunnel-client-api/ids.d.ts +6 -0
  76. package/lib/plugins/service-tunnel-client-api/ids.d.ts.map +1 -0
  77. package/lib/plugins/service-tunnel-client-api/ids.js +17 -0
  78. package/lib/plugins/service-tunnel-client-api/ids.js.map +1 -0
  79. package/lib/plugins/service-tunnel-client-api/index.d.ts +158 -0
  80. package/lib/plugins/service-tunnel-client-api/index.d.ts.map +1 -0
  81. package/lib/plugins/service-tunnel-client-api/index.js +591 -0
  82. package/lib/plugins/service-tunnel-client-api/index.js.map +1 -0
  83. package/lib/plugins/service-tunnel-client-api/registry.d.ts +36 -0
  84. package/lib/plugins/service-tunnel-client-api/registry.d.ts.map +1 -0
  85. package/lib/plugins/service-tunnel-client-api/registry.js +14 -0
  86. package/lib/plugins/service-tunnel-client-api/registry.js.map +1 -0
  87. package/lib/plugins/service-tunnel-client-api/schemas.d.ts +66 -0
  88. package/lib/plugins/service-tunnel-client-api/schemas.d.ts.map +1 -0
  89. package/lib/plugins/service-tunnel-client-api/schemas.js +56 -0
  90. package/lib/plugins/service-tunnel-client-api/schemas.js.map +1 -0
  91. package/lib/plugins/service-tunnel-web/http.d.ts +4 -0
  92. package/lib/plugins/service-tunnel-web/http.d.ts.map +1 -0
  93. package/lib/plugins/service-tunnel-web/http.js +119 -0
  94. package/lib/plugins/service-tunnel-web/http.js.map +1 -0
  95. package/lib/plugins/service-tunnel-web/index.d.ts +79 -0
  96. package/lib/plugins/service-tunnel-web/index.d.ts.map +1 -0
  97. package/lib/plugins/service-tunnel-web/index.js +278 -0
  98. package/lib/plugins/service-tunnel-web/index.js.map +1 -0
  99. package/lib/plugins/service-tunnel-web/verification.d.ts +27 -0
  100. package/lib/plugins/service-tunnel-web/verification.d.ts.map +1 -0
  101. package/lib/plugins/service-tunnel-web/verification.js +209 -0
  102. package/lib/plugins/service-tunnel-web/verification.js.map +1 -0
  103. package/lib/prisma.d.ts +6 -0
  104. package/lib/prisma.d.ts.map +1 -0
  105. package/lib/prisma.js +10 -0
  106. package/lib/prisma.js.map +1 -0
  107. package/lib/schemas/service-betterportal.json +109 -0
  108. package/lib/schemas/service-tunnel-client-api.json +300 -0
  109. package/lib/schemas/service-tunnel-web.json +140 -0
  110. package/package.json +60 -0
  111. package/prisma/migrations/0001_init/migration.sql +63 -0
  112. package/prisma/migrations/0002_account_plan/migration.sql +6 -0
  113. package/prisma/migrations/0003_tunnel_owner_server/migration.sql +1 -0
  114. package/prisma/migrations/20260701000000_add_client_auth/migration.sql +46 -0
  115. package/prisma/migrations/migration_lock.toml +3 -0
  116. package/prisma/schema.prisma +141 -0
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "jsx-htmx/jsx-runtime";
2
+ function formatBytes(value) {
3
+ if (value < 1024)
4
+ return `${value} B`;
5
+ if (value < 1024 * 1024)
6
+ return `${(value / 1024).toFixed(1)} KB`;
7
+ if (value < 1024 * 1024 * 1024)
8
+ return `${(value / 1024 / 1024).toFixed(1)} MB`;
9
+ return `${(value / 1024 / 1024 / 1024).toFixed(1)} GB`;
10
+ }
11
+ export function render(data) {
12
+ const metrics = [
13
+ { label: "Active tunnels", value: String(data.activeTunnels) },
14
+ { label: "Total tunnels", value: String(data.totalTunnels) },
15
+ { label: "Anonymous accounts", value: String(data.anonymousAccounts) },
16
+ { label: "Registered accounts", value: String(data.registeredAccounts) },
17
+ { label: "Requests", value: String(data.requests) },
18
+ { label: "Transfer", value: `${formatBytes(data.bytesIn)} in / ${formatBytes(data.bytesOut)} out` }
19
+ ];
20
+ return (_jsxs("section", { class: "container-fluid px-0", children: [_jsxs("div", { class: "d-flex justify-content-between align-items-start mb-4", children: [_jsxs("div", { children: [_jsx("h1", { class: "h3 mb-1", children: "BetterTunnels Dashboard" }), _jsx("p", { class: "text-body-secondary mb-0", children: "Platform health, usage, and recent audit activity." })] }), _jsx("a", { class: "btn btn-outline-secondary btn-sm", href: "/tunnels", children: "Tunnel sessions" })] }), _jsx("div", { class: "row g-3 mb-4", children: metrics.map((metric) => (_jsx("div", { class: "col-sm-6 col-xl-4", children: _jsxs("div", { class: "border rounded-3 p-3 bg-body h-100", children: [_jsx("div", { class: "small text-body-secondary mb-1", children: metric.label }), _jsx("div", { class: "h4 mb-0", children: metric.value })] }) }))) }), _jsxs("div", { class: "border rounded-3 bg-body", children: [_jsx("div", { class: "px-3 py-2 border-bottom fw-semibold", children: "Recent audit events" }), _jsx("div", { class: "table-responsive", children: _jsxs("table", { class: "table table-sm align-middle mb-0", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Event" }), _jsx("th", { children: "Subject" }), _jsx("th", { children: "Created" })] }) }), _jsx("tbody", { children: data.recentAuditEvents.length === 0 ? (_jsx("tr", { children: _jsx("td", { colspan: "3", class: "text-center text-body-secondary py-4", children: "No audit events yet." }) })) : data.recentAuditEvents.map((event) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx("code", { children: event.event }) }), _jsx("td", { class: "text-body-secondary", children: event.subjectId ?? "none" }), _jsx("td", { class: "text-body-secondary", children: new Date(event.createdAt).toLocaleString() })] }))) })] }) })] })] }));
21
+ }
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../src/plugins/service-betterportal/bp-routes/dashboard/_theme.bootstrap1/index.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import * as av from "anyvali";
2
+ import type { Infer } from "anyvali";
3
+ import { type ApiAuthRequirement, type CacheHints } from "@betterportal/framework";
4
+ export declare const viewId = "better-tunnels.dashboard";
5
+ export declare const title = "BetterTunnels Dashboard";
6
+ export declare const description = "Operational summary for BetterTunnels.";
7
+ export declare const auth: ApiAuthRequirement;
8
+ export declare const cacheHints: CacheHints;
9
+ export declare const ResponseSchema: av.ObjectSchema<{
10
+ activeTunnels: av.NumberSchema;
11
+ totalTunnels: av.NumberSchema;
12
+ anonymousAccounts: av.NumberSchema;
13
+ registeredAccounts: av.NumberSchema;
14
+ requests: av.NumberSchema;
15
+ bytesIn: av.NumberSchema;
16
+ bytesOut: av.NumberSchema;
17
+ recentAuditEvents: av.ArraySchema<av.ObjectSchema<{
18
+ id: av.StringSchema;
19
+ event: av.StringSchema;
20
+ subjectId: av.OptionalSchema<av.StringSchema>;
21
+ createdAt: av.StringSchema;
22
+ }>>;
23
+ }>;
24
+ export type ResponseData = Infer<typeof ResponseSchema>;
25
+ export declare const handleGet: import("@betterportal/framework").RouteHandler<Record<string, string>, Record<string, unknown>, Record<string, string>, Record<string, unknown>, {
26
+ activeTunnels: number;
27
+ totalTunnels: number;
28
+ anonymousAccounts: number;
29
+ registeredAccounts: number;
30
+ requests: number;
31
+ bytesIn: number;
32
+ bytesOut: number;
33
+ recentAuditEvents: {
34
+ id: string;
35
+ event: string;
36
+ createdAt: string;
37
+ subjectId?: string | undefined;
38
+ }[];
39
+ }, unknown, Record<string, unknown>>;
40
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/service-betterportal/bp-routes/dashboard/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,UAAU,EAChB,MAAM,yBAAyB,CAAC;AAGjC,eAAO,MAAM,MAAM,6BAA6B,CAAC;AACjD,eAAO,MAAM,KAAK,4BAA4B,CAAC;AAC/C,eAAO,MAAM,WAAW,2CAA2C,CAAC;AAEpE,eAAO,MAAM,IAAI,EAAE,kBAKlB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,UAGxB,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;EAcC,CAAC;AAC7B,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAExD,eAAO,MAAM,SAAS;;;;;;;;;;;;;;oCAmCrB,CAAC"}
@@ -0,0 +1,57 @@
1
+ import * as av from "anyvali";
2
+ import { createHandler } from "@betterportal/framework";
3
+ import { prisma } from "../../../../prisma.js";
4
+ export const viewId = "better-tunnels.dashboard";
5
+ export const title = "BetterTunnels Dashboard";
6
+ export const description = "Operational summary for BetterTunnels.";
7
+ export const auth = {
8
+ required: true,
9
+ permissions: [
10
+ { serviceId: "service.betterportal.tunnels", viewId, permissions: ["read"] }
11
+ ]
12
+ };
13
+ export const cacheHints = {
14
+ ttlSeconds: 5,
15
+ varyBy: ["accept", "authorization", "x-bp-tenant-id", "x-bp-app-id"]
16
+ };
17
+ export const ResponseSchema = av.object({
18
+ activeTunnels: av.number(),
19
+ totalTunnels: av.number(),
20
+ anonymousAccounts: av.number(),
21
+ registeredAccounts: av.number(),
22
+ requests: av.number(),
23
+ bytesIn: av.number(),
24
+ bytesOut: av.number(),
25
+ recentAuditEvents: av.array(av.object({
26
+ id: av.string(),
27
+ event: av.string(),
28
+ subjectId: av.optional(av.string()),
29
+ createdAt: av.string()
30
+ }, { unknownKeys: "strip" }))
31
+ }, { unknownKeys: "strip" });
32
+ export const handleGet = createHandler({ response: ResponseSchema }, async () => {
33
+ const [activeTunnels, totalTunnels, anonymousAccounts, registeredAccounts, usageRows, recentAuditEvents] = await Promise.all([
34
+ prisma.tunnel.count({ where: { status: "active" } }),
35
+ prisma.tunnel.count(),
36
+ prisma.account.count({ where: { plan: "anonymous" } }),
37
+ prisma.account.count({ where: { NOT: { plan: "anonymous" } } }),
38
+ prisma.usageCounter.findMany(),
39
+ prisma.auditEvent.findMany({ orderBy: { createdAt: "desc" }, take: 10 })
40
+ ]);
41
+ return {
42
+ activeTunnels,
43
+ totalTunnels,
44
+ anonymousAccounts,
45
+ registeredAccounts,
46
+ requests: usageRows.reduce((sum, row) => sum + row.requests, 0),
47
+ bytesIn: usageRows.reduce((sum, row) => sum + Number(row.bytesIn), 0),
48
+ bytesOut: usageRows.reduce((sum, row) => sum + Number(row.bytesOut), 0),
49
+ recentAuditEvents: recentAuditEvents.map((event) => ({
50
+ id: event.id,
51
+ event: event.event,
52
+ subjectId: event.subjectId ?? undefined,
53
+ createdAt: event.createdAt.toISOString()
54
+ }))
55
+ };
56
+ });
57
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/plugins/service-betterportal/bp-routes/dashboard/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** @jsxImportSource jsx-htmx */
2
+ import type { HtmlRenderable } from "@betterportal/framework";
3
+ import type { ResponseData } from "../index.js";
4
+ export declare function render(data: ResponseData): HtmlRenderable;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/plugins/service-betterportal/bp-routes/downloads/_theme.bootstrap1/index.tsx"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,cAAc,CAsDzD"}
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "jsx-htmx/jsx-runtime";
2
+ export function render(data) {
3
+ return (_jsxs("section", { class: "container-fluid px-0", children: [_jsxs("div", { class: "mb-4", children: [_jsx("img", { src: data.logoUrl, alt: "BetterTunnels", class: "img-fluid mb-3", style: "max-width: 280px; height: auto;" }), _jsx("h1", { class: "h3 mb-1", children: "BetterTunnels CLI" }), _jsx("p", { class: "text-body-secondary mb-0", children: "Download the CLI and start a public development tunnel." })] }), _jsxs("div", { class: "row g-4", children: [_jsx("div", { class: "col-lg-5", children: _jsxs("div", { class: "border rounded-3 p-4 bg-body h-100", children: [_jsx("h2", { class: "h5 mb-3", children: "Quick start" }), data.examples.map((example) => (_jsx("pre", { class: "small bg-body-tertiary border rounded-2 p-3 mb-2", children: _jsx("code", { children: example }) })))] }) }), _jsx("div", { class: "col-lg-7", children: _jsxs("div", { class: "border rounded-3 bg-body h-100", children: [_jsxs("div", { class: "px-3 py-2 border-bottom d-flex justify-content-between", children: [_jsx("span", { class: "fw-semibold", children: "Release assets" }), _jsx("span", { class: "text-body-secondary", children: data.latestTag })] }), _jsx("div", { class: "table-responsive", children: _jsxs("table", { class: "table table-sm align-middle mb-0", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "OS" }), _jsx("th", { children: "Arch" }), _jsx("th", { children: "Artifact" })] }) }), _jsx("tbody", { children: data.downloads.map((download) => (_jsxs("tr", { children: [_jsx("td", { children: download.os }), _jsx("td", { children: download.arch }), _jsx("td", { children: _jsx("code", { children: download.artifact }) })] }))) })] }) })] }) })] })] }));
4
+ }
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../src/plugins/service-betterportal/bp-routes/downloads/_theme.bootstrap1/index.tsx"],"names":[],"mappings":";AAIA,MAAM,UAAU,MAAM,CAAC,IAAkB;IACvC,OAAO,CACL,mBAAS,KAAK,EAAC,sBAAsB,aACnC,eAAK,KAAK,EAAC,MAAM,aACf,cACE,GAAG,EAAE,IAAI,CAAC,OAAO,EACjB,GAAG,EAAC,eAAe,EACnB,KAAK,EAAC,gBAAgB,EACtB,KAAK,EAAC,iCAAiC,GACvC,EACF,aAAI,KAAK,EAAC,SAAS,kCAAuB,EAC1C,YAAG,KAAK,EAAC,0BAA0B,wEAA4D,IAC3F,EAEN,eAAK,KAAK,EAAC,SAAS,aAClB,cAAK,KAAK,EAAC,UAAU,YACnB,eAAK,KAAK,EAAC,oCAAoC,aAC7C,aAAI,KAAK,EAAC,SAAS,4BAAiB,EACnC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAC9B,cAAK,KAAK,EAAC,kDAAkD,YAAC,yBAAO,OAAO,GAAQ,GAAM,CAC3F,CAAC,IACE,GACF,EACN,cAAK,KAAK,EAAC,UAAU,YACnB,eAAK,KAAK,EAAC,gCAAgC,aACzC,eAAK,KAAK,EAAC,wDAAwD,aACjE,eAAM,KAAK,EAAC,aAAa,+BAAsB,EAC/C,eAAM,KAAK,EAAC,qBAAqB,YAAE,IAAI,CAAC,SAAS,GAAQ,IACrD,EACN,cAAK,KAAK,EAAC,kBAAkB,YAC3B,iBAAO,KAAK,EAAC,kCAAkC,aAC7C,0BACE,yBACE,8BAAW,EACX,gCAAa,EACb,oCAAiB,IACd,GACC,EACR,0BACG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAChC,yBACE,uBAAK,QAAQ,CAAC,EAAE,GAAM,EACtB,uBAAK,QAAQ,CAAC,IAAI,GAAM,EACxB,uBAAI,yBAAO,QAAQ,CAAC,QAAQ,GAAQ,GAAK,IACtC,CACN,CAAC,GACI,IACF,GACJ,IACF,GACF,IACF,IACE,CACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,30 @@
1
+ import * as av from "anyvali";
2
+ import type { Infer } from "anyvali";
3
+ import { type ApiAuthRequirement, type CacheHints } from "@betterportal/framework";
4
+ export declare const viewId = "better-tunnels.downloads";
5
+ export declare const title = "BetterTunnels CLI";
6
+ export declare const description = "CLI download and setup commands.";
7
+ export declare const auth: ApiAuthRequirement;
8
+ export declare const cacheHints: CacheHints;
9
+ export declare const ResponseSchema: av.ObjectSchema<{
10
+ latestTag: av.StringSchema;
11
+ logoUrl: av.StringSchema;
12
+ examples: av.ArraySchema<av.StringSchema>;
13
+ downloads: av.ArraySchema<av.ObjectSchema<{
14
+ os: av.StringSchema;
15
+ arch: av.StringSchema;
16
+ artifact: av.StringSchema;
17
+ }>>;
18
+ }>;
19
+ export type ResponseData = Infer<typeof ResponseSchema>;
20
+ export declare const handleGet: import("@betterportal/framework").RouteHandler<Record<string, string>, Record<string, unknown>, Record<string, string>, Record<string, unknown>, {
21
+ latestTag: string;
22
+ logoUrl: string;
23
+ examples: string[];
24
+ downloads: {
25
+ os: string;
26
+ arch: string;
27
+ artifact: string;
28
+ }[];
29
+ }, unknown, Record<string, unknown>>;
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/service-betterportal/bp-routes/downloads/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,UAAU,EAChB,MAAM,yBAAyB,CAAC;AAGjC,eAAO,MAAM,MAAM,6BAA6B,CAAC;AACjD,eAAO,MAAM,KAAK,sBAAsB,CAAC;AACzC,eAAO,MAAM,WAAW,qCAAqC,CAAC;AAE9D,eAAO,MAAM,IAAI,EAAE,kBAGlB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,UAGxB,CAAC;AAQF,eAAO,MAAM,cAAc;;;;;;;;;EAKC,CAAC;AAC7B,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAExD,eAAO,MAAM,SAAS;;;;;;;;;oCAsBrB,CAAC"}
@@ -0,0 +1,45 @@
1
+ import * as av from "anyvali";
2
+ import { createHandler } from "@betterportal/framework";
3
+ import { loadBrandAssets } from "../../brand-assets.js";
4
+ export const viewId = "better-tunnels.downloads";
5
+ export const title = "BetterTunnels CLI";
6
+ export const description = "CLI download and setup commands.";
7
+ export const auth = {
8
+ required: false,
9
+ permissions: []
10
+ };
11
+ export const cacheHints = {
12
+ ttlSeconds: 60,
13
+ varyBy: ["accept", "x-bp-tenant-id", "x-bp-app-id"]
14
+ };
15
+ const DownloadSchema = av.object({
16
+ os: av.string(),
17
+ arch: av.string(),
18
+ artifact: av.string()
19
+ }, { unknownKeys: "strip" });
20
+ export const ResponseSchema = av.object({
21
+ latestTag: av.string(),
22
+ logoUrl: av.string(),
23
+ examples: av.array(av.string()),
24
+ downloads: av.array(DownloadSchema)
25
+ }, { unknownKeys: "strip" });
26
+ export const handleGet = createHandler({ response: ResponseSchema }, async () => {
27
+ const brand = await loadBrandAssets();
28
+ return {
29
+ latestTag: "v0.1.0",
30
+ logoUrl: brand.logoUrl,
31
+ examples: [
32
+ "better-tunnels tunnel --port 3100",
33
+ "better-tunnels tunnel --host 127.0.0.1 --port 3100",
34
+ "better-tunnels tunnel --config .bettertunnel"
35
+ ],
36
+ downloads: [
37
+ { os: "Windows", arch: "amd64", artifact: "better-tunnels-windows-amd64.exe" },
38
+ { os: "Linux", arch: "amd64", artifact: "better-tunnels-linux-amd64" },
39
+ { os: "Linux", arch: "arm64", artifact: "better-tunnels-linux-arm64" },
40
+ { os: "macOS", arch: "amd64", artifact: "better-tunnels-darwin-amd64" },
41
+ { os: "macOS", arch: "arm64", artifact: "better-tunnels-darwin-arm64" }
42
+ ]
43
+ };
44
+ });
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/plugins/service-betterportal/bp-routes/downloads/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,OAAO,EACL,aAAa,EAGd,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,CAAC,MAAM,MAAM,GAAG,0BAA0B,CAAC;AACjD,MAAM,CAAC,MAAM,KAAK,GAAG,mBAAmB,CAAC;AACzC,MAAM,CAAC,MAAM,WAAW,GAAG,kCAAkC,CAAC;AAE9D,MAAM,CAAC,MAAM,IAAI,GAAuB;IACtC,QAAQ,EAAE,KAAK;IACf,WAAW,EAAE,EAAE;CAChB,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAe;IACpC,UAAU,EAAE,EAAE;IACd,MAAM,EAAE,CAAC,QAAQ,EAAE,gBAAgB,EAAE,aAAa,CAAC;CACpD,CAAC;AAEF,MAAM,cAAc,GAAG,EAAE,CAAC,MAAM,CAAC;IAC/B,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE;IACf,IAAI,EAAE,EAAE,CAAC,MAAM,EAAE;IACjB,QAAQ,EAAE,EAAE,CAAC,MAAM,EAAE;CACtB,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;AAE7B,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC,MAAM,CAAC;IACtC,SAAS,EAAE,EAAE,CAAC,MAAM,EAAE;IACtB,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE;IACpB,QAAQ,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC;IAC/B,SAAS,EAAE,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC;CACpC,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;AAG7B,MAAM,CAAC,MAAM,SAAS,GAAG,aAAa,CACpC,EAAE,QAAQ,EAAE,cAAc,EAAE,EAC5B,KAAK,IAAI,EAAE;IACT,MAAM,KAAK,GAAG,MAAM,eAAe,EAAE,CAAC;IAEtC,OAAO;QACL,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,QAAQ,EAAE;YACR,mCAAmC;YACnC,oDAAoD;YACpD,8CAA8C;SAC/C;QACD,SAAS,EAAE;YACT,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,kCAAkC,EAAE;YAC9E,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,4BAA4B,EAAE;YACtE,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,4BAA4B,EAAE;YACtE,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,6BAA6B,EAAE;YACvE,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,6BAA6B,EAAE;SACxE;KACF,CAAC;AACJ,CAAC,CACF,CAAC"}
@@ -0,0 +1,5 @@
1
+ /** @jsxImportSource jsx-htmx */
2
+ import type { HtmlRenderable } from "@betterportal/framework";
3
+ import type { ResponseData } from "../index.js";
4
+ export declare function render(data: ResponseData): HtmlRenderable;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/plugins/service-betterportal/bp-routes/landing/_theme.bootstrap1/index.tsx"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,cAAc,CA2FzD"}
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "jsx-htmx/jsx-runtime";
2
+ export function render(data) {
3
+ return (_jsxs("main", { class: "container-fluid px-0", children: [_jsx("section", { class: "py-5", children: _jsxs("div", { class: "row align-items-center g-5", children: [_jsxs("div", { class: "col-lg-7", children: [_jsx("img", { src: data.logoUrl, alt: "BetterTunnels", class: "img-fluid mb-4", style: "max-width: 360px; height: auto;" }), _jsx("div", { class: "mb-3 text-uppercase fw-semibold text-danger small", children: data.domain }), _jsx("h1", { class: "display-4 fw-semibold mb-3", children: "Public dev tunnels with a verification gate" }), _jsx("p", { class: "lead text-body-secondary mb-4", children: "Public development tunnels with explicit visitor verification, operator visibility, and a CLI workflow designed for local projects." }), _jsxs("div", { class: "d-flex flex-wrap gap-2", children: [_jsx("a", { class: "btn btn-danger btn-lg", href: "/downloads", children: "Get the CLI" }), _jsx("a", { class: "btn btn-outline-secondary btn-lg", href: "/dashboard", children: "Open dashboard" })] })] }), _jsx("div", { class: "col-lg-5", children: _jsxs("div", { class: "border rounded-3 bg-body-tertiary p-3 shadow-sm", children: [_jsxs("div", { class: "d-flex align-items-center justify-content-between border-bottom pb-2 mb-3", children: [_jsxs("span", { class: "d-flex align-items-center gap-2 fw-semibold", children: [_jsx("img", { src: data.faviconUrl, alt: "", width: "24", height: "24" }), "local project"] }), _jsx("span", { class: "badge text-bg-danger", children: "public" })] }), _jsx("div", { class: "text-center py-3", children: _jsx("img", { src: data.markUrl, alt: "", class: "img-fluid rounded-3", style: "max-width: 180px;" }) }), _jsx("pre", { class: "mb-3 small", children: _jsx("code", { children: data.cliInstall[0] }) }), _jsxs("div", { class: "bg-body border rounded-2 p-3 small", children: [_jsx("div", { class: "text-body-secondary mb-1", children: "Tunnel URL" }), _jsxs("code", { children: ["https://a1b2c3-port3100-203-0-113-10.", data.domain] })] }), _jsxs("div", { class: "row g-2 mt-2 small", children: [_jsx("div", { class: "col-4", children: _jsx("div", { class: "border rounded-2 p-2 bg-body", children: "verify" }) }), _jsx("div", { class: "col-4", children: _jsx("div", { class: "border rounded-2 p-2 bg-body", children: "proxy" }) }), _jsx("div", { class: "col-4", children: _jsx("div", { class: "border rounded-2 p-2 bg-body", children: "trace" }) })] })] }) })] }) }), _jsx("section", { class: "py-4 border-top", children: _jsx("div", { class: "row g-3", children: data.highlights.map((item) => (_jsx("div", { class: "col-md-4", children: _jsxs("div", { class: "h-100 border rounded-3 p-4 bg-body", children: [_jsx("h2", { class: "h5 mb-2", children: item.title }), _jsx("p", { class: "text-body-secondary mb-0", children: item.body })] }) }))) }) }), _jsx("section", { class: "py-4 border-top", children: _jsxs("div", { class: "row g-4 align-items-start", children: [_jsxs("div", { class: "col-lg-5", children: [_jsx("h2", { class: "h4 mb-2", children: "Managed by BetterPortal" }), _jsx("p", { class: "text-body-secondary mb-0", children: "The public site, dashboard, and future account controls run as BetterPortal routes. Tunnel traffic can stay on separate infrastructure while this service manages the UI." })] }), _jsx("div", { class: "col-lg-7", children: _jsx("ul", { class: "list-group list-group-flush border rounded-3", children: data.limits.map((limit) => (_jsxs("li", { class: "list-group-item d-flex gap-2 align-items-start", children: [_jsx("span", { class: "text-danger fw-bold", children: "!" }), _jsx("span", { children: limit })] }))) }) })] }) })] }));
4
+ }
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../src/plugins/service-betterportal/bp-routes/landing/_theme.bootstrap1/index.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import * as av from "anyvali";
2
+ import type { Infer } from "anyvali";
3
+ import { type ApiAuthRequirement, type CacheHints } from "@betterportal/framework";
4
+ export declare const viewId = "better-tunnels.landing";
5
+ export declare const title = "BetterTunnels";
6
+ export declare const description = "Public landing page for BetterTunnels.";
7
+ export declare const auth: ApiAuthRequirement;
8
+ export declare const cacheHints: CacheHints;
9
+ export declare const ResponseSchema: av.ObjectSchema<{
10
+ product: av.StringSchema;
11
+ domain: av.StringSchema;
12
+ connectHost: av.StringSchema;
13
+ webHost: av.StringSchema;
14
+ logoUrl: av.StringSchema;
15
+ markUrl: av.StringSchema;
16
+ faviconUrl: av.StringSchema;
17
+ cliInstall: av.ArraySchema<av.StringSchema>;
18
+ highlights: av.ArraySchema<av.ObjectSchema<{
19
+ title: av.StringSchema;
20
+ body: av.StringSchema;
21
+ }>>;
22
+ limits: av.ArraySchema<av.StringSchema>;
23
+ }>;
24
+ export type ResponseData = Infer<typeof ResponseSchema>;
25
+ export declare const handleGet: import("@betterportal/framework").RouteHandler<Record<string, string>, Record<string, unknown>, Record<string, string>, Record<string, unknown>, {
26
+ product: string;
27
+ domain: string;
28
+ connectHost: string;
29
+ webHost: string;
30
+ logoUrl: string;
31
+ markUrl: string;
32
+ faviconUrl: string;
33
+ cliInstall: string[];
34
+ highlights: {
35
+ title: string;
36
+ body: string;
37
+ }[];
38
+ limits: string[];
39
+ }, unknown, Record<string, unknown>>;
40
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/service-betterportal/bp-routes/landing/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,UAAU,EAChB,MAAM,yBAAyB,CAAC;AAGjC,eAAO,MAAM,MAAM,2BAA2B,CAAC;AAC/C,eAAO,MAAM,KAAK,kBAAkB,CAAC;AACrC,eAAO,MAAM,WAAW,2CAA2C,CAAC;AAEpE,eAAO,MAAM,IAAI,EAAE,kBAGlB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,UAGxB,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;EAcC,CAAC;AAC7B,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAExD,eAAO,MAAM,SAAS;;;;;;;;;;;;;;oCAoCrB,CAAC"}
@@ -0,0 +1,63 @@
1
+ import * as av from "anyvali";
2
+ import { createHandler } from "@betterportal/framework";
3
+ import { loadBrandAssets } from "../../brand-assets.js";
4
+ export const viewId = "better-tunnels.landing";
5
+ export const title = "BetterTunnels";
6
+ export const description = "Public landing page for BetterTunnels.";
7
+ export const auth = {
8
+ required: false,
9
+ permissions: []
10
+ };
11
+ export const cacheHints = {
12
+ ttlSeconds: 60,
13
+ varyBy: ["accept", "x-bp-tenant-id", "x-bp-app-id"]
14
+ };
15
+ export const ResponseSchema = av.object({
16
+ product: av.string(),
17
+ domain: av.string(),
18
+ connectHost: av.string(),
19
+ webHost: av.string(),
20
+ logoUrl: av.string(),
21
+ markUrl: av.string(),
22
+ faviconUrl: av.string(),
23
+ cliInstall: av.array(av.string()),
24
+ highlights: av.array(av.object({
25
+ title: av.string(),
26
+ body: av.string()
27
+ }, { unknownKeys: "strip" })),
28
+ limits: av.array(av.string())
29
+ }, { unknownKeys: "strip" });
30
+ export const handleGet = createHandler({ response: ResponseSchema }, async () => {
31
+ const brand = await loadBrandAssets();
32
+ return {
33
+ product: "BetterTunnels",
34
+ domain: "tunnels.betterportal.dev",
35
+ connectHost: "connect.tunnels.betterportal.dev",
36
+ webHost: "betterportal.tunnels.betterportal.dev",
37
+ ...brand,
38
+ cliInstall: [
39
+ "better-tunnels tunnel --port 3100",
40
+ "better-tunnels tunnel --config .bettertunnel"
41
+ ],
42
+ highlights: [
43
+ {
44
+ title: "HTTP and HTTPS tunnels",
45
+ body: "Expose local development servers through generated, short-lived public URLs."
46
+ },
47
+ {
48
+ title: "Verification-first access",
49
+ body: "Visitors pass through a clear development-tunnel warning before reaching a tunnel."
50
+ },
51
+ {
52
+ title: "Built for operations",
53
+ body: "Structured request timings, trace-friendly sessions, and BetterPortal management routes."
54
+ }
55
+ ],
56
+ limits: [
57
+ "Anonymous sessions are temporary.",
58
+ "Tunnel subdomains are generated by the platform.",
59
+ "Registered accounts can unlock higher limits and verification controls later."
60
+ ]
61
+ };
62
+ });
63
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/plugins/service-betterportal/bp-routes/landing/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,OAAO,EACL,aAAa,EAGd,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,CAAC,MAAM,MAAM,GAAG,wBAAwB,CAAC;AAC/C,MAAM,CAAC,MAAM,KAAK,GAAG,eAAe,CAAC;AACrC,MAAM,CAAC,MAAM,WAAW,GAAG,wCAAwC,CAAC;AAEpE,MAAM,CAAC,MAAM,IAAI,GAAuB;IACtC,QAAQ,EAAE,KAAK;IACf,WAAW,EAAE,EAAE;CAChB,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAe;IACpC,UAAU,EAAE,EAAE;IACd,MAAM,EAAE,CAAC,QAAQ,EAAE,gBAAgB,EAAE,aAAa,CAAC;CACpD,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC,MAAM,CAAC;IACtC,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE;IACpB,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE;IACnB,WAAW,EAAE,EAAE,CAAC,MAAM,EAAE;IACxB,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE;IACpB,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE;IACpB,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE;IACpB,UAAU,EAAE,EAAE,CAAC,MAAM,EAAE;IACvB,UAAU,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC;IACjC,UAAU,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC;QAC7B,KAAK,EAAE,EAAE,CAAC,MAAM,EAAE;QAClB,IAAI,EAAE,EAAE,CAAC,MAAM,EAAE;KAClB,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;IAC7B,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC;CAC9B,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;AAG7B,MAAM,CAAC,MAAM,SAAS,GAAG,aAAa,CACpC,EAAE,QAAQ,EAAE,cAAc,EAAE,EAC5B,KAAK,IAAI,EAAE;IACT,MAAM,KAAK,GAAG,MAAM,eAAe,EAAE,CAAC;IAEtC,OAAO;QACL,OAAO,EAAE,eAAe;QACxB,MAAM,EAAE,0BAA0B;QAClC,WAAW,EAAE,kCAAkC;QAC/C,OAAO,EAAE,uCAAuC;QAChD,GAAG,KAAK;QACR,UAAU,EAAE;YACV,mCAAmC;YACnC,8CAA8C;SAC/C;QACD,UAAU,EAAE;YACV;gBACE,KAAK,EAAE,wBAAwB;gBAC/B,IAAI,EAAE,8EAA8E;aACrF;YACD;gBACE,KAAK,EAAE,2BAA2B;gBAClC,IAAI,EAAE,oFAAoF;aAC3F;YACD;gBACE,KAAK,EAAE,sBAAsB;gBAC7B,IAAI,EAAE,0FAA0F;aACjG;SACF;QACD,MAAM,EAAE;YACN,mCAAmC;YACnC,kDAAkD;YAClD,+EAA+E;SAChF;KACF,CAAC;AACJ,CAAC,CACF,CAAC"}
@@ -0,0 +1,5 @@
1
+ /** @jsxImportSource jsx-htmx */
2
+ import type { HtmlRenderable } from "@betterportal/framework";
3
+ import type { ResponseData } from "../index.js";
4
+ export declare function render(data: ResponseData): HtmlRenderable;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/plugins/service-betterportal/bp-routes/tunnels/_theme.bootstrap1/index.tsx"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,cAAc,CAmDzD"}
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "jsx-htmx/jsx-runtime";
2
+ export function render(data) {
3
+ return (_jsxs("section", { class: "container-fluid px-0", children: [_jsxs("div", { class: "d-flex justify-content-between align-items-start mb-4", children: [_jsxs("div", { children: [_jsx("h1", { class: "h3 mb-1", children: "BetterTunnels" }), _jsx("p", { class: "text-body-secondary mb-0", children: "Live tunnel sessions and recent usage." })] }), _jsxs("div", { class: "text-end", children: [_jsx("div", { class: "h4 mb-0", children: String(data.active) }), _jsxs("div", { class: "small text-body-secondary", children: ["active / ", String(data.total), " shown"] })] })] }), _jsx("div", { class: "card border-0 shadow-sm", children: _jsx("div", { class: "table-responsive", children: _jsxs("table", { class: "table table-sm table-hover align-middle mb-0", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Subdomain" }), _jsx("th", { children: "Target" }), _jsx("th", { children: "Status" }), _jsx("th", { children: "Mode" }), _jsx("th", { class: "text-end", children: "Requests" }), _jsx("th", { children: "Expires" })] }) }), _jsx("tbody", { children: data.tunnels.length === 0 ? (_jsx("tr", { children: _jsx("td", { colspan: "6", class: "text-center text-body-secondary py-4", children: "No tunnels yet." }) })) : data.tunnels.map((tunnel) => (_jsxs("tr", { children: [_jsxs("td", { children: [_jsx("code", { children: tunnel.subdomain }), _jsx("div", { class: "small text-body-secondary", children: tunnel.ownerServerId ?? "no owner" })] }), _jsx("td", { children: tunnel.target }), _jsx("td", { children: _jsx("span", { class: `badge ${tunnel.status === "active" ? "text-bg-success" : "text-bg-secondary"}`, children: tunnel.status }) }), _jsx("td", { children: tunnel.authenticated ? "authenticated" : "anonymous" }), _jsx("td", { class: "text-end", children: String(tunnel.requests) }), _jsx("td", { class: "small text-body-secondary", children: new Date(tunnel.expiresAt).toLocaleString() })] }))) })] }) }) })] }));
4
+ }
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../src/plugins/service-betterportal/bp-routes/tunnels/_theme.bootstrap1/index.tsx"],"names":[],"mappings":";AAIA,MAAM,UAAU,MAAM,CAAC,IAAkB;IACvC,OAAO,CACL,mBAAS,KAAK,EAAC,sBAAsB,aACnC,eAAK,KAAK,EAAC,uDAAuD,aAChE,0BACE,aAAI,KAAK,EAAC,SAAS,8BAAmB,EACtC,YAAG,KAAK,EAAC,0BAA0B,uDAA2C,IAC1E,EACN,eAAK,KAAK,EAAC,UAAU,aACnB,cAAK,KAAK,EAAC,SAAS,YAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAO,EAChD,eAAK,KAAK,EAAC,2BAA2B,0BAAW,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,cAAa,IAC5E,IACF,EAEN,cAAK,KAAK,EAAC,yBAAyB,YAClC,cAAK,KAAK,EAAC,kBAAkB,YAC3B,iBAAO,KAAK,EAAC,8CAA8C,aACzD,0BACE,yBACE,qCAAkB,EAClB,kCAAe,EACf,kCAAe,EACf,gCAAa,EACb,aAAI,KAAK,EAAC,UAAU,yBAAc,EAClC,mCAAgB,IACb,GACC,EACR,0BACG,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAC3B,uBACE,aAAI,OAAO,EAAC,GAAG,EAAC,KAAK,EAAC,sCAAsC,gCAAqB,GAC9E,CACN,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAC/B,yBACE,yBACE,yBAAO,MAAM,CAAC,SAAS,GAAQ,EAC/B,cAAK,KAAK,EAAC,2BAA2B,YAAE,MAAM,CAAC,aAAa,IAAI,UAAU,GAAO,IAC9E,EACL,uBAAK,MAAM,CAAC,MAAM,GAAM,EACxB,uBAAI,eAAM,KAAK,EAAE,SAAS,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,mBAAmB,EAAE,YAAG,MAAM,CAAC,MAAM,GAAQ,GAAK,EAC7H,uBAAK,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,WAAW,GAAM,EAC/D,aAAI,KAAK,EAAC,UAAU,YAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAM,EACnD,aAAI,KAAK,EAAC,2BAA2B,YAAE,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,cAAc,EAAE,GAAM,IACrF,CACN,CAAC,GACI,IACF,GACJ,GACF,IACE,CACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,44 @@
1
+ import * as av from "anyvali";
2
+ import type { Infer } from "anyvali";
3
+ import { type ApiAuthRequirement, type CacheHints } from "@betterportal/framework";
4
+ export declare const viewId = "better-tunnels.tunnels";
5
+ export declare const title = "BetterTunnels";
6
+ export declare const description = "Tunnel sessions, status, and recent activity.";
7
+ export declare const auth: ApiAuthRequirement;
8
+ export declare const cacheHints: CacheHints;
9
+ export declare const ResponseSchema: av.ObjectSchema<{
10
+ active: av.NumberSchema;
11
+ total: av.NumberSchema;
12
+ tunnels: av.ArraySchema<av.ObjectSchema<{
13
+ id: av.StringSchema;
14
+ subdomain: av.StringSchema;
15
+ target: av.StringSchema;
16
+ authenticated: av.BoolSchema;
17
+ status: av.StringSchema;
18
+ ownerServerId: av.OptionalSchema<av.StringSchema>;
19
+ createdAt: av.StringSchema;
20
+ expiresAt: av.StringSchema;
21
+ requests: av.NumberSchema;
22
+ bytesIn: av.NumberSchema;
23
+ bytesOut: av.NumberSchema;
24
+ }>>;
25
+ }>;
26
+ export type ResponseData = Infer<typeof ResponseSchema>;
27
+ export declare const handleGet: import("@betterportal/framework").RouteHandler<Record<string, string>, Record<string, unknown>, Record<string, string>, Record<string, unknown>, {
28
+ active: number;
29
+ total: number;
30
+ tunnels: {
31
+ id: string;
32
+ subdomain: string;
33
+ target: string;
34
+ authenticated: boolean;
35
+ status: string;
36
+ createdAt: string;
37
+ expiresAt: string;
38
+ requests: number;
39
+ bytesIn: number;
40
+ bytesOut: number;
41
+ ownerServerId?: string | undefined;
42
+ }[];
43
+ }, unknown, Record<string, unknown>>;
44
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/service-betterportal/bp-routes/tunnels/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,UAAU,EAChB,MAAM,yBAAyB,CAAC;AAGjC,eAAO,MAAM,MAAM,2BAA2B,CAAC;AAC/C,eAAO,MAAM,KAAK,kBAAkB,CAAC;AACrC,eAAO,MAAM,WAAW,kDAAkD,CAAC;AAE3E,eAAO,MAAM,IAAI,EAAE,kBAKlB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,UAGxB,CAAC;AAgBF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;EAIC,CAAC;AAC7B,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAExD,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;oCA2BrB,CAAC"}
@@ -0,0 +1,59 @@
1
+ import * as av from "anyvali";
2
+ import { createHandler } from "@betterportal/framework";
3
+ import { prisma } from "../../../../prisma.js";
4
+ export const viewId = "better-tunnels.tunnels";
5
+ export const title = "BetterTunnels";
6
+ export const description = "Tunnel sessions, status, and recent activity.";
7
+ export const auth = {
8
+ required: true,
9
+ permissions: [
10
+ { serviceId: "service.betterportal.tunnels", viewId, permissions: ["read"] }
11
+ ]
12
+ };
13
+ export const cacheHints = {
14
+ ttlSeconds: 5,
15
+ varyBy: ["accept", "authorization", "x-bp-tenant-id", "x-bp-app-id"]
16
+ };
17
+ const TunnelRowSchema = av.object({
18
+ id: av.string(),
19
+ subdomain: av.string(),
20
+ target: av.string(),
21
+ authenticated: av.bool(),
22
+ status: av.string(),
23
+ ownerServerId: av.optional(av.string()),
24
+ createdAt: av.string(),
25
+ expiresAt: av.string(),
26
+ requests: av.number(),
27
+ bytesIn: av.number(),
28
+ bytesOut: av.number()
29
+ }, { unknownKeys: "strip" });
30
+ export const ResponseSchema = av.object({
31
+ active: av.number(),
32
+ total: av.number(),
33
+ tunnels: av.array(TunnelRowSchema)
34
+ }, { unknownKeys: "strip" });
35
+ export const handleGet = createHandler({ response: ResponseSchema }, async () => {
36
+ const rows = await prisma.tunnel.findMany({
37
+ orderBy: { createdAt: "desc" },
38
+ take: 50,
39
+ include: { usage: true }
40
+ });
41
+ return {
42
+ active: rows.filter((row) => row.status === "active").length,
43
+ total: rows.length,
44
+ tunnels: rows.map((row) => ({
45
+ id: row.id,
46
+ subdomain: row.subdomain,
47
+ target: `${row.targetHost}:${row.targetPort}`,
48
+ authenticated: row.authenticated,
49
+ status: row.status,
50
+ ownerServerId: row.ownerServerId ?? undefined,
51
+ createdAt: row.createdAt.toISOString(),
52
+ expiresAt: row.expiresAt.toISOString(),
53
+ requests: row.usage?.requests ?? 0,
54
+ bytesIn: Number(row.usage?.bytesIn ?? 0n),
55
+ bytesOut: Number(row.usage?.bytesOut ?? 0n)
56
+ }))
57
+ };
58
+ });
59
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/plugins/service-betterportal/bp-routes/tunnels/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export type BrandAssets = {
2
+ logoUrl: string;
3
+ markUrl: string;
4
+ faviconUrl: string;
5
+ };
6
+ export declare function loadBrandAssets(): Promise<BrandAssets>;
7
+ //# sourceMappingURL=brand-assets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-assets.d.ts","sourceRoot":"","sources":["../../../src/plugins/service-betterportal/brand-assets.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAMF,wBAAsB,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC,CAgB5D"}
@@ -0,0 +1,24 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ let cached;
5
+ const assetRoot = join(dirname(fileURLToPath(import.meta.url)), "assets/brand");
6
+ export async function loadBrandAssets() {
7
+ if (cached)
8
+ return cached;
9
+ const [logo, mark, favicon] = await Promise.all([
10
+ readFile(join(assetRoot, "bettertunnels-horizontal-light.svg"), "utf8"),
11
+ readFile(join(assetRoot, "bettertunnels-square-dark-1024x1024.png")),
12
+ readFile(join(assetRoot, "favicon.svg"), "utf8")
13
+ ]);
14
+ cached = {
15
+ logoUrl: svgDataUrl(logo),
16
+ markUrl: `data:image/png;base64,${mark.toString("base64")}`,
17
+ faviconUrl: svgDataUrl(favicon)
18
+ };
19
+ return cached;
20
+ }
21
+ function svgDataUrl(value) {
22
+ return `data:image/svg+xml;base64,${Buffer.from(value).toString("base64")}`;
23
+ }
24
+ //# sourceMappingURL=brand-assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-assets.js","sourceRoot":"","sources":["../../../src/plugins/service-betterportal/brand-assets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAQzC,IAAI,MAA+B,CAAC;AAEpC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;AAEhF,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC9C,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,oCAAoC,CAAC,EAAE,MAAM,CAAC;QACvE,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,yCAAyC,CAAC,CAAC;QACpE,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC;KACjD,CAAC,CAAC;IAEH,MAAM,GAAG;QACP,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC;QACzB,OAAO,EAAE,yBAAyB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAC3D,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC;KAChC,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,6BAA6B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC9E,CAAC"}