@lukoweb/apitogo 0.1.55 → 0.1.57
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/dist/cli/cli.js +280 -46
- package/dist/declarations/config/default-landing-content.d.ts +2 -0
- package/dist/declarations/config/landing-manifest.d.ts +6 -0
- package/dist/declarations/config/local-manifest.d.ts +6 -0
- package/dist/declarations/config/resolve-modules.d.ts +3 -1
- package/dist/declarations/config/validators/ModulesSchema.d.ts +32 -0
- package/dist/declarations/config/validators/ZudokuConfig.d.ts +7 -0
- package/dist/declarations/lib/authentication/providers/dev-portal-utils.d.ts +1 -0
- package/dist/flat-config.d.ts +53 -40
- package/package.json +1 -1
- package/src/config/default-landing-content.ts +154 -0
- package/src/config/resolve-modules.ts +115 -47
- package/src/config/validators/ModulesSchema.ts +25 -0
- package/src/lib/authentication/providers/dev-portal-utils.ts +9 -0
- package/src/vite/plugin-theme.ts +41 -0
package/dist/flat-config.d.ts
CHANGED
|
@@ -64,7 +64,7 @@ export type _Schema15 = boolean
|
|
|
64
64
|
/**
|
|
65
65
|
* Gateway plan catalog for /pricing preview and publish (canonical storage).
|
|
66
66
|
*/
|
|
67
|
-
export type
|
|
67
|
+
export type _Schema37 = {
|
|
68
68
|
sku: string
|
|
69
69
|
displayName: string
|
|
70
70
|
priceAmount?: number
|
|
@@ -73,31 +73,31 @@ export type _Schema35 = {
|
|
|
73
73
|
limitsJson?: string
|
|
74
74
|
includedActionKeys?: string[]
|
|
75
75
|
}[]
|
|
76
|
-
export type
|
|
76
|
+
export type _Schema38 = ({
|
|
77
77
|
type: "url"
|
|
78
|
-
input: (string |
|
|
79
|
-
server?:
|
|
80
|
-
path?:
|
|
81
|
-
categories?:
|
|
82
|
-
options?:
|
|
78
|
+
input: (string | _Schema39[])
|
|
79
|
+
server?: _Schema40
|
|
80
|
+
path?: _Schema41
|
|
81
|
+
categories?: _Schema42
|
|
82
|
+
options?: _Schema44
|
|
83
83
|
} | {
|
|
84
84
|
type: "file"
|
|
85
|
-
input: (string | (string |
|
|
86
|
-
server?:
|
|
87
|
-
path?:
|
|
88
|
-
categories?:
|
|
89
|
-
options?:
|
|
85
|
+
input: (string | (string | _Schema39)[])
|
|
86
|
+
server?: _Schema40
|
|
87
|
+
path?: _Schema41
|
|
88
|
+
categories?: _Schema42
|
|
89
|
+
options?: _Schema44
|
|
90
90
|
} | {
|
|
91
91
|
type: "raw"
|
|
92
92
|
input: string
|
|
93
|
-
server?:
|
|
94
|
-
path?:
|
|
95
|
-
categories?:
|
|
96
|
-
options?:
|
|
93
|
+
server?: _Schema40
|
|
94
|
+
path?: _Schema41
|
|
95
|
+
categories?: _Schema42
|
|
96
|
+
options?: _Schema44
|
|
97
97
|
})
|
|
98
|
-
export type
|
|
99
|
-
export type
|
|
100
|
-
export type
|
|
98
|
+
export type _Schema40 = string
|
|
99
|
+
export type _Schema41 = string
|
|
100
|
+
export type _Schema42 = {
|
|
101
101
|
label: string
|
|
102
102
|
tags: string[]
|
|
103
103
|
}[]
|
|
@@ -396,10 +396,10 @@ export interface FlatZudokuConfig {
|
|
|
396
396
|
modules?: {
|
|
397
397
|
docs?: _Schema14
|
|
398
398
|
landing?: _Schema16
|
|
399
|
-
userPanel?:
|
|
399
|
+
userPanel?: _Schema25
|
|
400
400
|
}
|
|
401
|
-
apis?: (
|
|
402
|
-
catalogs?: (
|
|
401
|
+
apis?: (_Schema38 | _Schema38[])
|
|
402
|
+
catalogs?: (_Schema45 | _Schema45[])
|
|
403
403
|
apiKeys?: {
|
|
404
404
|
enabled: boolean
|
|
405
405
|
getConsumers?: unknown
|
|
@@ -427,7 +427,7 @@ export interface FlatZudokuConfig {
|
|
|
427
427
|
}
|
|
428
428
|
enableStatusPages?: boolean
|
|
429
429
|
defaults?: {
|
|
430
|
-
apis:
|
|
430
|
+
apis: _Schema44
|
|
431
431
|
examplesLanguage?: string
|
|
432
432
|
}
|
|
433
433
|
__pluginDirs?: string[]
|
|
@@ -525,6 +525,10 @@ export interface _Schema16 {
|
|
|
525
525
|
*/
|
|
526
526
|
layout?: ("default" | "landing" | "none")
|
|
527
527
|
content?: _Schema21
|
|
528
|
+
/**
|
|
529
|
+
* When true, merge DEFAULT_LANDING_CONTENT for unset sections. When false or omitted, only configured sections are shown.
|
|
530
|
+
*/
|
|
531
|
+
useDefaults?: boolean
|
|
528
532
|
}
|
|
529
533
|
/**
|
|
530
534
|
* Default landing page content when no custom component is provided.
|
|
@@ -532,6 +536,10 @@ export interface _Schema16 {
|
|
|
532
536
|
export interface _Schema21 {
|
|
533
537
|
hero?: {
|
|
534
538
|
title?: string
|
|
539
|
+
/**
|
|
540
|
+
* Accent-colored word or phrase shown on a new line after the title.
|
|
541
|
+
*/
|
|
542
|
+
titleAccent?: string
|
|
535
543
|
subtitle?: string
|
|
536
544
|
description?: string
|
|
537
545
|
badge?: string
|
|
@@ -542,13 +550,18 @@ export interface _Schema21 {
|
|
|
542
550
|
code: string
|
|
543
551
|
}
|
|
544
552
|
}
|
|
553
|
+
featuresSection?: {
|
|
554
|
+
label?: string
|
|
555
|
+
title?: string
|
|
556
|
+
description?: string
|
|
557
|
+
}
|
|
545
558
|
features?: {
|
|
546
559
|
title: string
|
|
547
560
|
description: string
|
|
548
561
|
icon?: string
|
|
549
562
|
}[]
|
|
550
|
-
primaryAction?:
|
|
551
|
-
secondaryAction?:
|
|
563
|
+
primaryAction?: _Schema23
|
|
564
|
+
secondaryAction?: _Schema23
|
|
552
565
|
ticker?: {
|
|
553
566
|
enabled?: boolean
|
|
554
567
|
items?: {
|
|
@@ -581,19 +594,19 @@ export interface _Schema21 {
|
|
|
581
594
|
enabled?: boolean
|
|
582
595
|
title?: string
|
|
583
596
|
description?: string
|
|
584
|
-
primaryAction?:
|
|
585
|
-
secondaryAction?:
|
|
597
|
+
primaryAction?: _Schema23
|
|
598
|
+
secondaryAction?: _Schema23
|
|
586
599
|
}
|
|
587
600
|
showPoweredBy?: boolean
|
|
588
601
|
}
|
|
589
|
-
export interface
|
|
602
|
+
export interface _Schema23 {
|
|
590
603
|
label: string
|
|
591
604
|
href: string
|
|
592
605
|
}
|
|
593
606
|
/**
|
|
594
607
|
* User panel module — authenticated user area with sub-modules.
|
|
595
608
|
*/
|
|
596
|
-
export interface
|
|
609
|
+
export interface _Schema25 {
|
|
597
610
|
enabled?: _Schema15
|
|
598
611
|
/**
|
|
599
612
|
* Base path prefix for user panel routes.
|
|
@@ -615,7 +628,7 @@ export interface _Schema23 {
|
|
|
615
628
|
component?: {
|
|
616
629
|
[k: string]: unknown
|
|
617
630
|
}
|
|
618
|
-
content?:
|
|
631
|
+
content?: _Schema32
|
|
619
632
|
}
|
|
620
633
|
userManagement?: {
|
|
621
634
|
enabled?: _Schema15
|
|
@@ -635,7 +648,7 @@ export interface _Schema23 {
|
|
|
635
648
|
component?: {
|
|
636
649
|
[k: string]: unknown
|
|
637
650
|
}
|
|
638
|
-
content?:
|
|
651
|
+
content?: _Schema33
|
|
639
652
|
}
|
|
640
653
|
apiKeys?: {
|
|
641
654
|
enabled?: _Schema15
|
|
@@ -660,30 +673,30 @@ export interface _Schema23 {
|
|
|
660
673
|
component?: {
|
|
661
674
|
[k: string]: unknown
|
|
662
675
|
}
|
|
663
|
-
content?:
|
|
664
|
-
items?:
|
|
676
|
+
content?: _Schema36
|
|
677
|
+
items?: _Schema37
|
|
665
678
|
}
|
|
666
679
|
}
|
|
667
680
|
/**
|
|
668
681
|
* Default dashboard page content when no custom component is provided.
|
|
669
682
|
*/
|
|
670
|
-
export interface
|
|
683
|
+
export interface _Schema32 {
|
|
671
684
|
title?: string
|
|
672
685
|
subtitle?: string
|
|
673
686
|
description?: string
|
|
674
|
-
quickLinks?:
|
|
687
|
+
quickLinks?: _Schema23[]
|
|
675
688
|
}
|
|
676
689
|
/**
|
|
677
690
|
* Default profile page content when no custom component is provided.
|
|
678
691
|
*/
|
|
679
|
-
export interface
|
|
692
|
+
export interface _Schema33 {
|
|
680
693
|
title?: string
|
|
681
694
|
description?: string
|
|
682
695
|
}
|
|
683
696
|
/**
|
|
684
697
|
* Default plans page content when no custom component is provided.
|
|
685
698
|
*/
|
|
686
|
-
export interface
|
|
699
|
+
export interface _Schema36 {
|
|
687
700
|
title?: string
|
|
688
701
|
description?: string
|
|
689
702
|
tiers?: {
|
|
@@ -692,12 +705,12 @@ export interface _Schema34 {
|
|
|
692
705
|
description?: string
|
|
693
706
|
}[]
|
|
694
707
|
}
|
|
695
|
-
export interface
|
|
708
|
+
export interface _Schema39 {
|
|
696
709
|
path: string
|
|
697
710
|
input: string
|
|
698
711
|
label?: string
|
|
699
712
|
}
|
|
700
|
-
export interface
|
|
713
|
+
export interface _Schema44 {
|
|
701
714
|
examplesLanguage?: string
|
|
702
715
|
supportedLanguages?: {
|
|
703
716
|
value: string
|
|
@@ -714,7 +727,7 @@ export interface _Schema42 {
|
|
|
714
727
|
transformExamples?: unknown
|
|
715
728
|
generateCodeSnippet?: unknown
|
|
716
729
|
}
|
|
717
|
-
export interface
|
|
730
|
+
export interface _Schema45 {
|
|
718
731
|
path: string
|
|
719
732
|
label: string
|
|
720
733
|
items?: string[]
|
package/package.json
CHANGED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { ResolvedLandingContent } from "./validators/ModulesSchema.js";
|
|
2
|
+
|
|
3
|
+
/** Default landing sections aligned with webapp-template/APIToGo.dc.html */
|
|
4
|
+
export const DEFAULT_LANDING_CONTENT: ResolvedLandingContent = {
|
|
5
|
+
hero: {
|
|
6
|
+
badge: "99.98% uptime",
|
|
7
|
+
subtitle: "Production-ready APIs, one endpoint away",
|
|
8
|
+
title: "Developer APIs that ship",
|
|
9
|
+
titleAccent: "today.",
|
|
10
|
+
description:
|
|
11
|
+
"Documentation, interactive API reference, auth, and billing — sub-40ms responses, one API key, generous free tier.",
|
|
12
|
+
highlights: ["No credit card", "10k requests free", "SDKs for 6 languages"],
|
|
13
|
+
codeExample: {
|
|
14
|
+
filename: "example.sh",
|
|
15
|
+
language: "bash",
|
|
16
|
+
code: `$ curl https://api.example.com/v1/items \\
|
|
17
|
+
-H "Authorization: Bearer YOUR_API_KEY" \\
|
|
18
|
+
-d "limit=10"
|
|
19
|
+
|
|
20
|
+
{
|
|
21
|
+
"items": [
|
|
22
|
+
{ "id": "item_1", "name": "Widget", "status": "active" },
|
|
23
|
+
{ "id": "item_2", "name": "Gadget", "status": "active" }
|
|
24
|
+
]
|
|
25
|
+
} // 28ms`,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
primaryAction: { label: "Get your API key", href: "/register" },
|
|
29
|
+
secondaryAction: { label: "Read the docs", href: "/introduction" },
|
|
30
|
+
ticker: {
|
|
31
|
+
items: [
|
|
32
|
+
{ label: "GET", value: "/v1/items", change: "28ms" },
|
|
33
|
+
{ label: "POST", value: "/v1/items", change: "34ms" },
|
|
34
|
+
{ label: "GET", value: "/v1/users", change: "19ms" },
|
|
35
|
+
{ label: "PATCH", value: "/v1/users/{id}", change: "41ms" },
|
|
36
|
+
{ label: "GET", value: "/v1/health", change: "16ms" },
|
|
37
|
+
{ label: "DELETE", value: "/v1/items/{id}", change: "22ms" },
|
|
38
|
+
{ label: "GET", value: "/v1/search", change: "47ms" },
|
|
39
|
+
{ label: "POST", value: "/v1/auth/token", change: "52ms" },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
trust: {
|
|
43
|
+
title: "Trusted by teams shipping production APIs",
|
|
44
|
+
logos: ["Acme Corp", "BuildFast", "DataFlow", "ShipKit", "DevStack", "LaunchPad"],
|
|
45
|
+
},
|
|
46
|
+
featuresSection: {
|
|
47
|
+
label: "// why us",
|
|
48
|
+
title: "Everything you need to build on your API",
|
|
49
|
+
description:
|
|
50
|
+
"Skip the infra. We handle docs, auth, keys, and billing so you can focus on your product.",
|
|
51
|
+
},
|
|
52
|
+
features: [
|
|
53
|
+
{
|
|
54
|
+
icon: "gauge",
|
|
55
|
+
title: "Sub-40ms responses",
|
|
56
|
+
description:
|
|
57
|
+
"Edge-cached and globally distributed, so your app feels instant anywhere.",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
icon: "radio",
|
|
61
|
+
title: "Real-time streams",
|
|
62
|
+
description:
|
|
63
|
+
"WebSocket feeds with automatic reconnection for live data and events.",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
icon: "blocks",
|
|
67
|
+
title: "Unified schema",
|
|
68
|
+
description:
|
|
69
|
+
"One consistent API design — no per-endpoint glue code or custom adapters.",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
icon: "shield-check",
|
|
73
|
+
title: "99.98% uptime",
|
|
74
|
+
description:
|
|
75
|
+
"Redundant infra with a public status page and an SLA on paid plans.",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
icon: "code",
|
|
79
|
+
title: "SDKs for 6 languages",
|
|
80
|
+
description:
|
|
81
|
+
"Typed clients for JS, Python, Go, Rust, Ruby and PHP. Copy, paste, ship.",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
icon: "webhook",
|
|
85
|
+
title: "Webhooks & alerts",
|
|
86
|
+
description:
|
|
87
|
+
"Push events and threshold alerts straight to your endpoints.",
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
apiShowcase: {
|
|
91
|
+
title: "Production APIs. One key.",
|
|
92
|
+
browseAllLabel: "Browse all APIs",
|
|
93
|
+
browseAllHref: "/api",
|
|
94
|
+
autoFromOpenApi: true,
|
|
95
|
+
items: [
|
|
96
|
+
{
|
|
97
|
+
name: "List resources",
|
|
98
|
+
path: "/v1/items",
|
|
99
|
+
icon: "boxes",
|
|
100
|
+
latency: "28ms",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "Create resource",
|
|
104
|
+
path: "/v1/items",
|
|
105
|
+
icon: "badge-dollar-sign",
|
|
106
|
+
latency: "34ms",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "Get resource",
|
|
110
|
+
path: "/v1/items/{id}",
|
|
111
|
+
icon: "layers",
|
|
112
|
+
latency: "19ms",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: "Search",
|
|
116
|
+
path: "/v1/search",
|
|
117
|
+
icon: "arrow-left-right",
|
|
118
|
+
latency: "41ms",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: "User profile",
|
|
122
|
+
path: "/v1/users/me",
|
|
123
|
+
icon: "wallet",
|
|
124
|
+
latency: "52ms",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: "Health check",
|
|
128
|
+
path: "/v1/health",
|
|
129
|
+
icon: "activity",
|
|
130
|
+
latency: "16ms",
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "Metadata",
|
|
134
|
+
path: "/v1/meta",
|
|
135
|
+
icon: "coins",
|
|
136
|
+
latency: "22ms",
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: "Rate limits",
|
|
140
|
+
path: "/v1/limits",
|
|
141
|
+
icon: "gauge",
|
|
142
|
+
latency: "16ms",
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
pricingCta: {
|
|
147
|
+
title: "Start free. Scale when you do.",
|
|
148
|
+
description:
|
|
149
|
+
"10,000 requests a month, free forever. Upgrade only when your app takes off.",
|
|
150
|
+
primaryAction: { label: "Create free account", href: "/register" },
|
|
151
|
+
secondaryAction: { label: "See pricing", href: "/pricing" },
|
|
152
|
+
},
|
|
153
|
+
showPoweredBy: true,
|
|
154
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_LANDING_CONTENT } from "./default-landing-content.js";
|
|
1
2
|
import {
|
|
2
3
|
type ResolvedModulesConfig,
|
|
3
4
|
ModulesSchema,
|
|
@@ -32,72 +33,137 @@ export const joinPanelPath = (basePath: string, routePath: string): string => {
|
|
|
32
33
|
return `/${`${base}/${route}`.replace(/^\/+/, "")}`;
|
|
33
34
|
};
|
|
34
35
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
title: "API Reference",
|
|
43
|
-
description:
|
|
44
|
-
"Publish interactive OpenAPI docs with a built-in playground and code samples.",
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
title: "Developer Portal",
|
|
48
|
-
description:
|
|
49
|
-
"Extend with landing pages, authentication, API keys, and subscription modules.",
|
|
50
|
-
},
|
|
51
|
-
];
|
|
36
|
+
const mergeLandingSection = <T extends { enabled?: boolean }>(
|
|
37
|
+
defaults: T | undefined,
|
|
38
|
+
override: T | undefined,
|
|
39
|
+
): T | undefined => {
|
|
40
|
+
if (override?.enabled === false) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
52
43
|
|
|
53
|
-
|
|
44
|
+
if (!defaults && !override) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { ...defaults, ...override } as T;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const buildDefaultLandingContent = (
|
|
54
52
|
config: ZudokuConfig,
|
|
55
|
-
content?: LandingContentConfig,
|
|
56
53
|
): ResolvedLandingContent => {
|
|
57
54
|
const siteTitle =
|
|
58
55
|
config.metadata?.applicationName ?? config.site?.title ?? "APIToGo";
|
|
59
56
|
|
|
60
|
-
|
|
57
|
+
return {
|
|
58
|
+
...DEFAULT_LANDING_CONTENT,
|
|
61
59
|
hero: {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
description:
|
|
65
|
-
content?.hero?.description ??
|
|
66
|
-
config.metadata?.description ??
|
|
67
|
-
"Ship beautiful API documentation and developer experiences with APIToGo.",
|
|
68
|
-
badge: content?.hero?.badge,
|
|
69
|
-
highlights: content?.hero?.highlights,
|
|
70
|
-
codeExample: content?.hero?.codeExample,
|
|
60
|
+
...DEFAULT_LANDING_CONTENT.hero,
|
|
61
|
+
title: `${siteTitle} APIs that ship`,
|
|
71
62
|
},
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
label: "Get started",
|
|
63
|
+
primaryAction: {
|
|
64
|
+
...DEFAULT_LANDING_CONTENT.primaryAction,
|
|
75
65
|
href: "/introduction",
|
|
66
|
+
label: "Get started",
|
|
76
67
|
},
|
|
77
|
-
secondaryAction:
|
|
78
|
-
|
|
68
|
+
secondaryAction: {
|
|
69
|
+
...DEFAULT_LANDING_CONTENT.secondaryAction,
|
|
79
70
|
href: "/api",
|
|
71
|
+
label: "API Reference",
|
|
80
72
|
},
|
|
81
|
-
|
|
73
|
+
pricingCta: DEFAULT_LANDING_CONTENT.pricingCta
|
|
74
|
+
? {
|
|
75
|
+
...DEFAULT_LANDING_CONTENT.pricingCta,
|
|
76
|
+
primaryAction: {
|
|
77
|
+
label: "Get started",
|
|
78
|
+
href: "/introduction",
|
|
79
|
+
},
|
|
80
|
+
secondaryAction: {
|
|
81
|
+
label: "See pricing",
|
|
82
|
+
href: "/pricing",
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
: undefined,
|
|
82
86
|
};
|
|
87
|
+
};
|
|
83
88
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
export const resolveLandingContent = (
|
|
90
|
+
config: ZudokuConfig,
|
|
91
|
+
content?: LandingContentConfig,
|
|
92
|
+
options?: { useDefaults?: boolean },
|
|
93
|
+
): ResolvedLandingContent => {
|
|
94
|
+
const siteTitle =
|
|
95
|
+
config.metadata?.applicationName ?? config.site?.title ?? "APIToGo";
|
|
87
96
|
|
|
88
|
-
if (
|
|
89
|
-
|
|
97
|
+
if (options?.useDefaults === true) {
|
|
98
|
+
const defaults = buildDefaultLandingContent(config);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
hero: {
|
|
102
|
+
...defaults.hero,
|
|
103
|
+
...content?.hero,
|
|
104
|
+
title: content?.hero?.title ?? defaults.hero?.title,
|
|
105
|
+
description:
|
|
106
|
+
content?.hero?.description ??
|
|
107
|
+
config.metadata?.description ??
|
|
108
|
+
defaults.hero?.description,
|
|
109
|
+
codeExample: content?.hero?.codeExample ?? defaults.hero?.codeExample,
|
|
110
|
+
titleAccent:
|
|
111
|
+
content?.hero?.titleAccent ??
|
|
112
|
+
(content?.hero?.title ? undefined : defaults.hero?.titleAccent),
|
|
113
|
+
},
|
|
114
|
+
featuresSection: mergeLandingSection(
|
|
115
|
+
defaults.featuresSection,
|
|
116
|
+
content?.featuresSection,
|
|
117
|
+
),
|
|
118
|
+
features: content?.features ?? defaults.features,
|
|
119
|
+
primaryAction: content?.primaryAction ?? defaults.primaryAction,
|
|
120
|
+
secondaryAction: content?.secondaryAction ?? defaults.secondaryAction,
|
|
121
|
+
showPoweredBy: content?.showPoweredBy ?? defaults.showPoweredBy,
|
|
122
|
+
ticker: mergeLandingSection(defaults.ticker, content?.ticker),
|
|
123
|
+
trust: mergeLandingSection(defaults.trust, content?.trust),
|
|
124
|
+
apiShowcase: mergeLandingSection(
|
|
125
|
+
defaults.apiShowcase,
|
|
126
|
+
content?.apiShowcase,
|
|
127
|
+
),
|
|
128
|
+
pricingCta: mergeLandingSection(defaults.pricingCta, content?.pricingCta),
|
|
129
|
+
};
|
|
90
130
|
}
|
|
91
131
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
132
|
+
const hasContent =
|
|
133
|
+
content !== undefined && Object.keys(content).length > 0;
|
|
95
134
|
|
|
96
|
-
if (
|
|
97
|
-
|
|
135
|
+
if (!hasContent) {
|
|
136
|
+
return {
|
|
137
|
+
hero: { title: siteTitle },
|
|
138
|
+
showPoweredBy: true,
|
|
139
|
+
};
|
|
98
140
|
}
|
|
99
141
|
|
|
100
|
-
return
|
|
142
|
+
return {
|
|
143
|
+
hero: {
|
|
144
|
+
...content.hero,
|
|
145
|
+
title: content.hero?.title ?? siteTitle,
|
|
146
|
+
},
|
|
147
|
+
featuresSection: content.featuresSection
|
|
148
|
+
? mergeLandingSection(undefined, content.featuresSection)
|
|
149
|
+
: undefined,
|
|
150
|
+
features: content.features,
|
|
151
|
+
primaryAction: content.primaryAction,
|
|
152
|
+
secondaryAction: content.secondaryAction,
|
|
153
|
+
showPoweredBy: content.showPoweredBy ?? true,
|
|
154
|
+
ticker: content.ticker
|
|
155
|
+
? mergeLandingSection(undefined, content.ticker)
|
|
156
|
+
: undefined,
|
|
157
|
+
trust: content.trust
|
|
158
|
+
? mergeLandingSection(undefined, content.trust)
|
|
159
|
+
: undefined,
|
|
160
|
+
apiShowcase: content.apiShowcase
|
|
161
|
+
? mergeLandingSection(undefined, content.apiShowcase)
|
|
162
|
+
: undefined,
|
|
163
|
+
pricingCta: content.pricingCta
|
|
164
|
+
? mergeLandingSection(undefined, content.pricingCta)
|
|
165
|
+
: undefined,
|
|
166
|
+
};
|
|
101
167
|
};
|
|
102
168
|
|
|
103
169
|
const DEFAULT_DASHBOARD_QUICK_LINKS: ResolvedDashboardContent["quickLinks"] = [
|
|
@@ -193,7 +259,9 @@ export const resolveModulesConfig = (
|
|
|
193
259
|
path: landingModule?.path ?? "/",
|
|
194
260
|
component: landingModule?.component,
|
|
195
261
|
layout: landingModule?.layout ?? "landing",
|
|
196
|
-
content: resolveLandingContent(config, landingModule?.content
|
|
262
|
+
content: resolveLandingContent(config, landingModule?.content, {
|
|
263
|
+
useDefaults: landingModule?.useDefaults === true,
|
|
264
|
+
}),
|
|
197
265
|
},
|
|
198
266
|
userPanel: {
|
|
199
267
|
enabled: userPanelEnabled,
|
|
@@ -58,6 +58,10 @@ export const LandingCodeExampleSchema = z
|
|
|
58
58
|
export const LandingHeroSchema = z
|
|
59
59
|
.object({
|
|
60
60
|
title: z.string().optional(),
|
|
61
|
+
titleAccent: z
|
|
62
|
+
.string()
|
|
63
|
+
.optional()
|
|
64
|
+
.describe("Accent-colored word or phrase shown on a new line after the title."),
|
|
61
65
|
subtitle: z.string().optional(),
|
|
62
66
|
description: z.string().optional(),
|
|
63
67
|
badge: z.string().optional(),
|
|
@@ -66,6 +70,14 @@ export const LandingHeroSchema = z
|
|
|
66
70
|
})
|
|
67
71
|
.optional();
|
|
68
72
|
|
|
73
|
+
export const LandingFeaturesSectionSchema = z
|
|
74
|
+
.object({
|
|
75
|
+
label: z.string().optional(),
|
|
76
|
+
title: z.string().optional(),
|
|
77
|
+
description: z.string().optional(),
|
|
78
|
+
})
|
|
79
|
+
.optional();
|
|
80
|
+
|
|
69
81
|
export const LandingTickerItemSchema = z.object({
|
|
70
82
|
label: z.string(),
|
|
71
83
|
value: z.string(),
|
|
@@ -120,6 +132,7 @@ export const LandingPricingCtaSchema = z
|
|
|
120
132
|
export const LandingContentSchema = z
|
|
121
133
|
.object({
|
|
122
134
|
hero: LandingHeroSchema,
|
|
135
|
+
featuresSection: LandingFeaturesSectionSchema,
|
|
123
136
|
features: z.array(LandingFeatureSchema).optional(),
|
|
124
137
|
primaryAction: LandingLinkSchema.optional(),
|
|
125
138
|
secondaryAction: LandingLinkSchema.optional(),
|
|
@@ -195,6 +208,12 @@ export const LandingModuleSchema = ModuleToggleSchema.extend({
|
|
|
195
208
|
content: LandingContentSchema.describe(
|
|
196
209
|
"Default landing page content when no custom component is provided.",
|
|
197
210
|
),
|
|
211
|
+
useDefaults: z
|
|
212
|
+
.boolean()
|
|
213
|
+
.optional()
|
|
214
|
+
.describe(
|
|
215
|
+
"When true, merge DEFAULT_LANDING_CONTENT for unset sections. When false or omitted, only configured sections are shown.",
|
|
216
|
+
),
|
|
198
217
|
});
|
|
199
218
|
|
|
200
219
|
export const UserManagementModuleSchema = ModuleToggleSchema.extend({
|
|
@@ -326,12 +345,18 @@ export type LandingPricingCtaConfig = z.infer<typeof LandingPricingCtaSchema>;
|
|
|
326
345
|
export type ResolvedLandingContent = {
|
|
327
346
|
hero?: {
|
|
328
347
|
title?: string;
|
|
348
|
+
titleAccent?: string;
|
|
329
349
|
subtitle?: string;
|
|
330
350
|
description?: string;
|
|
331
351
|
badge?: string;
|
|
332
352
|
highlights?: string[];
|
|
333
353
|
codeExample?: NonNullable<LandingCodeExampleConfig>;
|
|
334
354
|
};
|
|
355
|
+
featuresSection?: {
|
|
356
|
+
label?: string;
|
|
357
|
+
title?: string;
|
|
358
|
+
description?: string;
|
|
359
|
+
};
|
|
335
360
|
features?: LandingFeatureConfig[];
|
|
336
361
|
primaryAction?: LandingLinkConfig;
|
|
337
362
|
secondaryAction?: LandingLinkConfig;
|
|
@@ -66,6 +66,15 @@ export function buildDevPortalOidcChallengeUrl(
|
|
|
66
66
|
return url.toString();
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
export function buildDevPortalLoginUrl(
|
|
70
|
+
apiBaseUrl: string,
|
|
71
|
+
returnUrl: string,
|
|
72
|
+
): string {
|
|
73
|
+
const url = new URL("/api/v1/auth/login", apiBaseUrl);
|
|
74
|
+
url.searchParams.set("returnUrl", toAbsoluteReturnUrl(returnUrl));
|
|
75
|
+
return url.toString();
|
|
76
|
+
}
|
|
77
|
+
|
|
69
78
|
export function buildDevPortalLogoutUrl(
|
|
70
79
|
apiBaseUrl: string,
|
|
71
80
|
returnUrl: string,
|