@platformatic/composer 3.4.1 → 3.5.1
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/LICENSE +1 -1
- package/config.d.ts +482 -131
- package/eslint.config.js +4 -2
- package/index.d.ts +1 -17
- package/index.js +9 -210
- package/package.json +18 -59
- package/schema.json +2121 -843
- package/scripts/schema.js +12 -0
- package/.c8rc +0 -6
- package/composer.mjs +0 -54
- package/help/create.txt +0 -11
- package/help/help.txt +0 -7
- package/help/openapi schemas fetch.txt +0 -9
- package/help/start.txt +0 -54
- package/index.test-d.ts +0 -23
- package/lib/composer-hook.js +0 -60
- package/lib/create.mjs +0 -84
- package/lib/errors.js +0 -13
- package/lib/generator/README.md +0 -30
- package/lib/generator/composer-generator.d.ts +0 -11
- package/lib/generator/composer-generator.js +0 -128
- package/lib/graphql-fetch.js +0 -85
- package/lib/graphql-generator.js +0 -31
- package/lib/graphql.js +0 -20
- package/lib/openapi-composer.js +0 -81
- package/lib/openapi-config-schema.js +0 -93
- package/lib/openapi-fetch-schemas.mjs +0 -61
- package/lib/openapi-generator.js +0 -167
- package/lib/openapi-load-config.js +0 -31
- package/lib/openapi-modifier.js +0 -137
- package/lib/openapi.js +0 -49
- package/lib/proxy.js +0 -161
- package/lib/root-endpoint/index.js +0 -28
- package/lib/root-endpoint/public/images/dark_mode.svg +0 -3
- package/lib/root-endpoint/public/images/favicon.ico +0 -0
- package/lib/root-endpoint/public/images/light_mode.svg +0 -11
- package/lib/root-endpoint/public/images/platformatic-logo-dark.svg +0 -30
- package/lib/root-endpoint/public/images/platformatic-logo-light.svg +0 -30
- package/lib/root-endpoint/public/images/triangle_dark.svg +0 -3
- package/lib/root-endpoint/public/images/triangle_light.svg +0 -3
- package/lib/root-endpoint/public/index.html +0 -237
- package/lib/schema.js +0 -210
- package/lib/stackable.js +0 -59
- package/lib/upgrade.js +0 -22
- package/lib/utils.js +0 -27
- package/lib/versions/2.0.0.js +0 -11
package/lib/proxy.js
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { getGlobalDispatcher } = require('undici')
|
|
4
|
-
const httpProxy = require('@fastify/http-proxy')
|
|
5
|
-
const fp = require('fastify-plugin')
|
|
6
|
-
|
|
7
|
-
const kITC = Symbol.for('plt.runtime.itc')
|
|
8
|
-
|
|
9
|
-
async function resolveServiceProxyParameters (service) {
|
|
10
|
-
// Get meta information from the service, if any, to eventually hook up to a TCP port
|
|
11
|
-
const meta = (await globalThis[kITC]?.send('getServiceMeta', service.id))?.composer ?? { prefix: service.id }
|
|
12
|
-
const origin = meta.tcp ? meta.url : service.origin
|
|
13
|
-
|
|
14
|
-
// If no prefix could be found, assume the service id
|
|
15
|
-
const prefix = (service.proxy?.prefix ?? meta.prefix ?? service.id).replace(/(\/$)/g, '')
|
|
16
|
-
|
|
17
|
-
let rewritePrefix = ''
|
|
18
|
-
let internalRewriteLocationHeader = true
|
|
19
|
-
|
|
20
|
-
if (meta.wantsAbsoluteUrls) {
|
|
21
|
-
// The rewritePrefix purposely ignores service.proxy?.prefix to let
|
|
22
|
-
// the service always being able to configure their value
|
|
23
|
-
rewritePrefix = meta.prefix ?? service.id
|
|
24
|
-
internalRewriteLocationHeader = false
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return { origin, prefix, rewritePrefix, internalRewriteLocationHeader, needsRootRedirect: meta.needsRootRedirect }
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
module.exports = fp(async function (app, opts) {
|
|
31
|
-
const meta = { proxies: {} }
|
|
32
|
-
|
|
33
|
-
for (const service of opts.services) {
|
|
34
|
-
if (!service.proxy) {
|
|
35
|
-
// When a service defines no expose config at all
|
|
36
|
-
// we assume a proxy exposed with a prefix equals to its id or meta.prefix
|
|
37
|
-
if (service.proxy === false || service.openapi || service.graphql) {
|
|
38
|
-
continue
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const parameters = await resolveServiceProxyParameters(service)
|
|
43
|
-
const { prefix, origin, rewritePrefix, internalRewriteLocationHeader, needsRootRedirect } = parameters
|
|
44
|
-
meta.proxies[service.id] = parameters
|
|
45
|
-
|
|
46
|
-
const basePath = `/${prefix ?? ''}`.replaceAll(/\/+/g, '/').replace(/\/$/, '')
|
|
47
|
-
const dispatcher = getGlobalDispatcher()
|
|
48
|
-
|
|
49
|
-
if (needsRootRedirect) {
|
|
50
|
-
app.addHook('preHandler', (req, reply, done) => {
|
|
51
|
-
if (req.url === basePath) {
|
|
52
|
-
app.inject(
|
|
53
|
-
{
|
|
54
|
-
method: req.method,
|
|
55
|
-
url: `${basePath}/`,
|
|
56
|
-
headers: req.headers,
|
|
57
|
-
payload: req.body
|
|
58
|
-
},
|
|
59
|
-
(err, result) => {
|
|
60
|
-
if (err) {
|
|
61
|
-
done(err)
|
|
62
|
-
return
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const replyHeaders = result.headers
|
|
66
|
-
delete replyHeaders['content-length']
|
|
67
|
-
delete replyHeaders['transfer-encoding']
|
|
68
|
-
|
|
69
|
-
reply.code(result.statusCode).headers(replyHeaders).send(result.rawPayload)
|
|
70
|
-
done()
|
|
71
|
-
}
|
|
72
|
-
)
|
|
73
|
-
} else {
|
|
74
|
-
done()
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
}
|
|
78
|
-
/*
|
|
79
|
-
Some frontends, like Astro (https://github.com/withastro/astro/issues/11445)
|
|
80
|
-
generate invalid paths in development mode which ignore the basePath.
|
|
81
|
-
In that case we try to properly redirect the browser by trying to understand the prefix
|
|
82
|
-
from the Referer header.
|
|
83
|
-
*/
|
|
84
|
-
app.addHook('preHandler', (req, reply, done) => {
|
|
85
|
-
// If the URL is already targeted to the service, do nothing
|
|
86
|
-
if (req.url.startsWith(basePath)) {
|
|
87
|
-
done()
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Use the referer to understand the desired intent
|
|
92
|
-
const referer = req.headers.referer
|
|
93
|
-
|
|
94
|
-
if (!referer) {
|
|
95
|
-
done()
|
|
96
|
-
return
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const path = new URL(referer).pathname
|
|
100
|
-
|
|
101
|
-
// If we have a match redirect
|
|
102
|
-
if (path.startsWith(basePath)) {
|
|
103
|
-
reply.redirect(`${basePath}${req.url}`, 308)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
done()
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
// Do not show proxied services in Swagger
|
|
110
|
-
if (!service.openapi) {
|
|
111
|
-
app.addHook('onRoute', routeOptions => {
|
|
112
|
-
if (routeOptions.url.startsWith(basePath)) {
|
|
113
|
-
routeOptions.schema ??= {}
|
|
114
|
-
routeOptions.schema.hide = true
|
|
115
|
-
}
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
await app.register(httpProxy, {
|
|
120
|
-
prefix,
|
|
121
|
-
rewritePrefix,
|
|
122
|
-
upstream: origin,
|
|
123
|
-
websocket: true,
|
|
124
|
-
undici: dispatcher,
|
|
125
|
-
destroyAgent: false,
|
|
126
|
-
internalRewriteLocationHeader,
|
|
127
|
-
replyOptions: {
|
|
128
|
-
rewriteRequestHeaders: (request, headers) => {
|
|
129
|
-
const targetUrl = `${origin}${request.url}`
|
|
130
|
-
const context = request.span?.context
|
|
131
|
-
const { span, telemetryHeaders } = app.openTelemetry?.startHTTPSpanClient(
|
|
132
|
-
targetUrl,
|
|
133
|
-
request.method,
|
|
134
|
-
context
|
|
135
|
-
) || { span: null, telemetryHeaders: {} }
|
|
136
|
-
// We need to store the span in a different object
|
|
137
|
-
// to correctly close it in the onResponse hook
|
|
138
|
-
// Note that we have 2 spans:
|
|
139
|
-
// - request.span: the span of the request to the proxy
|
|
140
|
-
// - request.proxedCallSpan: the span of the request to the proxied service
|
|
141
|
-
request.proxedCallSpan = span
|
|
142
|
-
|
|
143
|
-
headers = {
|
|
144
|
-
...headers,
|
|
145
|
-
...telemetryHeaders,
|
|
146
|
-
'x-forwarded-for': request.ip,
|
|
147
|
-
'x-forwarded-host': request.host
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return headers
|
|
151
|
-
},
|
|
152
|
-
onResponse: (request, reply, res) => {
|
|
153
|
-
app.openTelemetry?.endHTTPSpanClient(reply.request.proxedCallSpan, { statusCode: reply.statusCode })
|
|
154
|
-
reply.send(res.stream)
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
})
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
opts.context?.stackable?.registerMeta(meta)
|
|
161
|
-
})
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { join } = require('node:path')
|
|
4
|
-
const fastifyStatic = require('@fastify/static')
|
|
5
|
-
const userAgentParser = require('my-ua-parser')
|
|
6
|
-
|
|
7
|
-
module.exports = async (app, opts) => {
|
|
8
|
-
app.register(fastifyStatic, {
|
|
9
|
-
root: join(__dirname, 'public'),
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
// root endpoint
|
|
13
|
-
app.route({
|
|
14
|
-
method: 'GET',
|
|
15
|
-
path: '/',
|
|
16
|
-
schema: { hide: true },
|
|
17
|
-
handler: (req, reply) => {
|
|
18
|
-
const uaString = req.headers['user-agent']
|
|
19
|
-
if (uaString) {
|
|
20
|
-
const parsed = userAgentParser(uaString)
|
|
21
|
-
if (parsed.browser.name !== undefined) {
|
|
22
|
-
return reply.sendFile('./index.html')
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return { message: 'Welcome to Platformatic! Please visit https://docs.platformatic.dev' }
|
|
26
|
-
},
|
|
27
|
-
})
|
|
28
|
-
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<path d="M5.38034 22.9538C3.01077 12.1815 12.2916 6.49615 17.2282 5C14.7599 8.49103 11.3043 16.9692 17.2282 22.9538C23.1521 28.9385 31.5444 25.4474 35 22.9538C33.8152 32.5292 25.6205 34.9231 21.6712 34.9231C17.2282 35.4218 7.74991 33.7262 5.38034 22.9538Z" stroke="#00283D" stroke-width="2" stroke-linejoin="round"/>
|
|
3
|
-
</svg>
|
|
Binary file
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<path d="M26 13L31 8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
3
|
-
<path d="M9 8L14 13" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
4
|
-
<path d="M14 28L9 33" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
5
|
-
<path d="M31 33L26 28" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
6
|
-
<path d="M10.0713 20L5 20" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
7
|
-
<circle cx="20.0713" cy="20" r="6" stroke="white" stroke-width="2"/>
|
|
8
|
-
<path d="M30.0712 20.0001L35 20" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
9
|
-
<path d="M20 29.9287L20 34.9999" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
10
|
-
<path d="M20.0001 9.92876L20 4.99999" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
11
|
-
</svg>
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<svg id="Logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 568.18 568.18">
|
|
3
|
-
<defs>
|
|
4
|
-
<style>
|
|
5
|
-
.cls-1 {
|
|
6
|
-
stroke: #00fe84;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
.cls-1, .cls-2 {
|
|
10
|
-
fill: none;
|
|
11
|
-
stroke-linecap: round;
|
|
12
|
-
stroke-linejoin: round;
|
|
13
|
-
stroke-width: 16.34px;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.cls-2 {
|
|
17
|
-
stroke: #fff;
|
|
18
|
-
}
|
|
19
|
-
</style>
|
|
20
|
-
</defs>
|
|
21
|
-
<g>
|
|
22
|
-
<line class="cls-1" x1="148.26" y1="317.46" x2="148.26" y2="389.92"/>
|
|
23
|
-
<polyline class="cls-1" points="176.75 303.19 234.68 336.63 321.11 286.74 321.11 186.94 234.68 137.04 148.26 186.94 148.26 273.55"/>
|
|
24
|
-
<polyline class="cls-1" points="176.75 334.87 176.75 303.19 205.15 285.91"/>
|
|
25
|
-
</g>
|
|
26
|
-
<polyline class="cls-2" points="466.14 259.75 501.19 280.47 501.19 301.18 466.14 321.89"/>
|
|
27
|
-
<polyline class="cls-2" points="106.23 210.58 66.98 233.24 66.98 334.38 105.4 356.56"/>
|
|
28
|
-
<polyline class="cls-2" points="176.75 376.88 176.75 399.34 234.33 431.14 278.53 405.35 278.17 358.41"/>
|
|
29
|
-
<polyline class="cls-2" points="321.66 332.06 321.66 356.76 363.05 380.98 423.55 344.88 423.55 246.39 364.01 212.25"/>
|
|
30
|
-
</svg>
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<svg id="Logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 568.18 568.18">
|
|
3
|
-
<defs>
|
|
4
|
-
<style>
|
|
5
|
-
.cls-1 {
|
|
6
|
-
stroke: #00050b;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
.cls-1, .cls-2 {
|
|
10
|
-
fill: none;
|
|
11
|
-
stroke-linecap: round;
|
|
12
|
-
stroke-linejoin: round;
|
|
13
|
-
stroke-width: 16.73px;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.cls-2 {
|
|
17
|
-
stroke: #00fe84;
|
|
18
|
-
}
|
|
19
|
-
</style>
|
|
20
|
-
</defs>
|
|
21
|
-
<g>
|
|
22
|
-
<line class="cls-2" x1="144.97" y1="318.27" x2="144.97" y2="392.48"/>
|
|
23
|
-
<polyline class="cls-2" points="174.15 303.65 233.49 337.91 322.01 286.8 322.01 184.58 233.49 133.48 144.97 184.58 144.97 273.29"/>
|
|
24
|
-
<polyline class="cls-2" points="174.15 336.1 174.15 303.66 203.24 285.96"/>
|
|
25
|
-
</g>
|
|
26
|
-
<polyline class="cls-1" points="470.56 259.16 506.46 280.38 506.46 301.59 470.56 322.81"/>
|
|
27
|
-
<polyline class="cls-1" points="101.92 208.8 61.72 232.01 61.72 335.6 101.07 358.32"/>
|
|
28
|
-
<polyline class="cls-1" points="174.15 379.13 174.15 402.14 233.12 434.7 278.4 408.29 278.03 360.22"/>
|
|
29
|
-
<polyline class="cls-1" points="322.57 333.23 322.57 358.53 364.96 383.33 426.93 346.35 426.93 245.48 365.94 210.51"/>
|
|
30
|
-
</svg>
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" type="image/svg+xml" href="./images/favicon.ico" />
|
|
6
|
-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800&display=swap" rel="stylesheet">
|
|
7
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
|
-
<title>Platformatic Composer</title>
|
|
9
|
-
<style>
|
|
10
|
-
body {
|
|
11
|
-
width: 100%;
|
|
12
|
-
height: 100vh;
|
|
13
|
-
overflow-y: auto;
|
|
14
|
-
overflow-x: hidden;
|
|
15
|
-
--secondary-color: #FFFFFF;
|
|
16
|
-
--primary-color: #00050B;
|
|
17
|
-
--primary-color-rgb: 0, 5, 11;
|
|
18
|
-
--secondary-color-rgb: 255, 255, 255;
|
|
19
|
-
--theme-img: url('./images/light_mode.svg');
|
|
20
|
-
--triangle-url: url('./images/triangle_light.svg');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
body.light-theme {
|
|
24
|
-
--secondary-color: #001825;
|
|
25
|
-
--primary-color: #FFFFFF;
|
|
26
|
-
--secondary-color-rgb: 0, 5, 11;
|
|
27
|
-
--primary-color-rgb: 255, 255, 255;
|
|
28
|
-
--theme-img: url('./images/dark_mode.svg');
|
|
29
|
-
--triangle-url: url('./images/triangle_dark.svg');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
body {
|
|
33
|
-
background: var(--primary-color);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
:root {
|
|
37
|
-
font-family: Montserrat, Inter, Avenir, Helvetica, Arial, sans-serif;
|
|
38
|
-
font-size: 16px;
|
|
39
|
-
line-height: 24px;
|
|
40
|
-
font-weight: 400;
|
|
41
|
-
|
|
42
|
-
color-scheme: light dark;
|
|
43
|
-
color: inherit;
|
|
44
|
-
|
|
45
|
-
position: relative;
|
|
46
|
-
font-synthesis: none;
|
|
47
|
-
text-rendering: optimizeLegibility;
|
|
48
|
-
-webkit-font-smoothing: antialiased;
|
|
49
|
-
-moz-osx-font-smoothing: grayscale;
|
|
50
|
-
-webkit-text-size-adjust: 100%;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
a {
|
|
55
|
-
font-weight: 600;
|
|
56
|
-
color: var(--secondary-color);
|
|
57
|
-
text-decoration: inherit;
|
|
58
|
-
width: 100%;
|
|
59
|
-
text-align: center;
|
|
60
|
-
padding: 4px 0px;
|
|
61
|
-
}
|
|
62
|
-
a:hover {
|
|
63
|
-
background-color: rgba(var(--secondary-color-rgb), 0.3);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.text-opacque {
|
|
67
|
-
opacity: 0.7;
|
|
68
|
-
}
|
|
69
|
-
.text-center {
|
|
70
|
-
text-align: center;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.button-container {
|
|
74
|
-
display: flex;
|
|
75
|
-
column-gap: 0.5rem;
|
|
76
|
-
justify-content: center;
|
|
77
|
-
align-items: start;
|
|
78
|
-
margin: 2rem 0;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.buttons-list-container {
|
|
82
|
-
display: flex;
|
|
83
|
-
flex-direction: column;
|
|
84
|
-
row-gap: 1rem;
|
|
85
|
-
justify-content: center;
|
|
86
|
-
align-items: start;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.button-link {
|
|
90
|
-
width: 246px;
|
|
91
|
-
border: 1px solid var(--secondary-color);
|
|
92
|
-
border-radius: 4px;
|
|
93
|
-
padding: 8px 16px;
|
|
94
|
-
cursor: pointer;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
button {
|
|
98
|
-
border-radius: 8px;
|
|
99
|
-
border: 1px solid transparent;
|
|
100
|
-
padding: 0.6em 1.2em;
|
|
101
|
-
font-size: 1em;
|
|
102
|
-
font-weight: 500;
|
|
103
|
-
font-family: inherit;
|
|
104
|
-
background-color: #1a1a1a;
|
|
105
|
-
cursor: pointer;
|
|
106
|
-
transition: border-color 0.25s;
|
|
107
|
-
}
|
|
108
|
-
button:hover {
|
|
109
|
-
border-color: #646cff;
|
|
110
|
-
}
|
|
111
|
-
button:focus,
|
|
112
|
-
button:focus-visible {
|
|
113
|
-
outline: 4px auto -webkit-focus-ring-color;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
#root {
|
|
117
|
-
width: 100%;
|
|
118
|
-
min-height: inherit;
|
|
119
|
-
height: 100%;
|
|
120
|
-
display: flex;
|
|
121
|
-
position: relative;
|
|
122
|
-
z-index: 20;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
#content {
|
|
126
|
-
display: flex;
|
|
127
|
-
flex-direction: column;
|
|
128
|
-
align-items: center;
|
|
129
|
-
justify-content: center;
|
|
130
|
-
margin: auto;
|
|
131
|
-
position: relative;
|
|
132
|
-
z-index: 20;
|
|
133
|
-
color: var(--secondary-color);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
.plt-triangle-container {
|
|
137
|
-
position: absolute;
|
|
138
|
-
top: 0;
|
|
139
|
-
right: 0;
|
|
140
|
-
width: 25%;
|
|
141
|
-
height: 50vH;
|
|
142
|
-
z-index: -1;
|
|
143
|
-
content: '';
|
|
144
|
-
background: var(--triangle-url) repeat;
|
|
145
|
-
opacity: 0.25;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
.plt-triangle-content-opacque {
|
|
149
|
-
position: absolute;
|
|
150
|
-
height: 100%;
|
|
151
|
-
width: 100%;
|
|
152
|
-
top: 0;
|
|
153
|
-
left: 0;
|
|
154
|
-
content: '';
|
|
155
|
-
background: linear-gradient(to top, rgba(var(--primary-color-rgb), 1), rgba(var(--primary-color-rgb), 0.2) 43%);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
#button-theme-selector {
|
|
159
|
-
border: none;
|
|
160
|
-
position: absolute;
|
|
161
|
-
top: 2rem;
|
|
162
|
-
right: 3rem;
|
|
163
|
-
width: 40px;
|
|
164
|
-
height: 40px;
|
|
165
|
-
background: var(--theme-img);
|
|
166
|
-
outline: none;
|
|
167
|
-
z-index: 1;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
.text-desktop-display {
|
|
171
|
-
font-family: Inter;
|
|
172
|
-
font-size: 4rem;
|
|
173
|
-
font-weight: 600;
|
|
174
|
-
line-height: 5rem;
|
|
175
|
-
text-align: center;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
.text-desktop-body-large {
|
|
179
|
-
font-family: Inter;
|
|
180
|
-
font-size: 1.125rem;
|
|
181
|
-
font-weight: 300;
|
|
182
|
-
line-height: 1.688rem;
|
|
183
|
-
text-align: center;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
</style>
|
|
187
|
-
</head>
|
|
188
|
-
<body>
|
|
189
|
-
<div id="root">
|
|
190
|
-
<div class="plt-triangle-container"><div class="plt-triangle-content-opacque"></div></div>
|
|
191
|
-
<button id="button-theme-selector" type="button" class="theme-selector" alt="theme selector" onclick="toggleLightMode()"></button>
|
|
192
|
-
|
|
193
|
-
<div id="content">
|
|
194
|
-
<img id="logo" height="256" />
|
|
195
|
-
<p class="text-desktop-display"><span>Welcome to</span><br/><span class="text-main-green">Platformatic Composer</span></p>
|
|
196
|
-
<p class="text-desktop-body-large">Explore our flexible toolkit for building robust APIs.</p>
|
|
197
|
-
<div class="button-container">
|
|
198
|
-
<a href="https://docs.platformatic.dev" target="_blank" class="button-link">Documentation</a>
|
|
199
|
-
<a id="openapi-link" target="_blank" class="button-link">OpenAPI Documentation</a>
|
|
200
|
-
<a id="graphql-link" target="_blank" class="button-link">GraphiQL</a>
|
|
201
|
-
</div>
|
|
202
|
-
</div>
|
|
203
|
-
</div>
|
|
204
|
-
|
|
205
|
-
<script>
|
|
206
|
-
let currentPath = window.location.pathname
|
|
207
|
-
|
|
208
|
-
if (!currentPath.endsWith('/')) {
|
|
209
|
-
currentPath += '/'
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const openApiLink = document.getElementById('openapi-link')
|
|
213
|
-
openApiLink.href = currentPath + 'documentation'
|
|
214
|
-
|
|
215
|
-
const graphqlLink = document.getElementById('graphql-link')
|
|
216
|
-
graphqlLink.href = currentPath + 'graphiql'
|
|
217
|
-
|
|
218
|
-
const prefersLightScheme = window.matchMedia('(prefers-color-scheme: light)');
|
|
219
|
-
if (prefersLightScheme.matches) {
|
|
220
|
-
document.body.classList.add('light-theme');
|
|
221
|
-
document.getElementById('logo').src = currentPath + 'images/platformatic-logo-light.svg'
|
|
222
|
-
} else {
|
|
223
|
-
document.body.classList.remove('light-theme');
|
|
224
|
-
document.getElementById('logo').src = currentPath + 'images/platformatic-logo-dark.svg'
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const toggleLightMode = function() {
|
|
228
|
-
document.body.classList.toggle('light-theme');
|
|
229
|
-
if (document.body.classList.contains('light-theme')) {
|
|
230
|
-
document.getElementById('logo').src = currentPath + 'images/platformatic-logo-light.svg'
|
|
231
|
-
} else {
|
|
232
|
-
document.getElementById('logo').src = currentPath + 'images/platformatic-logo-dark.svg'
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
</script>
|
|
236
|
-
</body>
|
|
237
|
-
</html>
|