From 2e05578e9ac4bf22fe9f988841f9f6116908541b Mon Sep 17 00:00:00 2001 From: Kohei Watanabe Date: Mon, 5 Sep 2022 21:12:35 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=AD=E3=82=B0=E3=82=A4=E3=83=B3=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=AA=E3=81=A3?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/config.ts | 49 ++ app/controllers/index.ts | 21 + app/controllers/routes/api.ts | 11 + app/controllers/routes/index.ts | 14 + app/controllers/routes/login.ts | 32 + app/controllers/routes/views.ts | 33 + app/main.ts | 60 +- app/package-lock.json | 1045 +++++++++++++++++++++++++++++++ app/package.json | 6 +- app/views/app.tsx | 35 +- app/views/env.d.ts | 2 +- app/views/pages/index.tsx | 4 +- app/views/pages/page.tsx | 22 +- compose.yml | 20 + db/migrations/0_init.sql | 50 +- db/schema.sql | 71 ++- docker-entrypoint.sh | 3 +- 17 files changed, 1401 insertions(+), 77 deletions(-) create mode 100644 app/controllers/config.ts create mode 100644 app/controllers/index.ts create mode 100644 app/controllers/routes/api.ts create mode 100644 app/controllers/routes/index.ts create mode 100644 app/controllers/routes/login.ts create mode 100644 app/controllers/routes/views.ts diff --git a/app/controllers/config.ts b/app/controllers/config.ts new file mode 100644 index 0000000..5a33e75 --- /dev/null +++ b/app/controllers/config.ts @@ -0,0 +1,49 @@ +import type { HttpOptions } from "openid-client"; +import crypto, { type KeyObject } from "node:crypto"; + +interface Config { + port: number; + apiUrl: URL; + apiEndpoint: string; + viewsDir: string; + rootUrl: URL; + openid: { + issuer: string; + client: { + client_id: string; + client_secret: string; + }; + request: HttpOptions; + }; + key: KeyObject; +} + +export type { Config }; + +function defaultConfig(): Config { + const port = Number(process.env.PORT ?? "8080"); + const rootUrl = new URL( + process.env.QUOT_ROOT_URL ?? `http://localhost:${port}/` + ); + return { + port, + rootUrl, + apiUrl: new URL(process.env.QUOT_API_URL ?? "http://127.0.0.1:3000"), + apiEndpoint: process.env.QUOT_API_ENDPOINT ?? "/api", + viewsDir: "views", + openid: { + issuer: process.env.QUOT_OPENID_ISSUER ?? "", + client: { + client_id: process.env.QUOT_OPENID_CLIENT_ID ?? "", + client_secret: process.env.QUOT_OPENID_CLIENT_SECRET ?? "", + }, + request: { timeout: 5_000 }, + }, + key: crypto.createPrivateKey({ + key: JSON.parse(process.env.QUOT_JWK ?? "{}"), + format: "jwk", + }), + }; +} + +export default defaultConfig; diff --git a/app/controllers/index.ts b/app/controllers/index.ts new file mode 100644 index 0000000..bd9cec0 --- /dev/null +++ b/app/controllers/index.ts @@ -0,0 +1,21 @@ +import fastify, { type FastifyInstance } from "fastify"; +import defaultConfig, { type Config } from "./config"; +import routes from "./routes/index"; + +declare module "fastify" { + interface FastifyInstance { + config: Config; + } +} + +export function App(config: Config = defaultConfig()): FastifyInstance { + const app = fastify({ logger: true }); + app.decorate("config", config).register(routes); + return app; +} + +export async function start(app: FastifyInstance): Promise { + await app.ready(); + const address = await app.listen({ host: "::", port: app.config.port }); + return address; +} diff --git a/app/controllers/routes/api.ts b/app/controllers/routes/api.ts new file mode 100644 index 0000000..6251484 --- /dev/null +++ b/app/controllers/routes/api.ts @@ -0,0 +1,11 @@ +import type { FastifyInstance } from "fastify"; +import httpProxy from "@fastify/http-proxy"; + +async function api(fastify: FastifyInstance) { + await fastify.register(httpProxy, { + prefix: "/api", + upstream: fastify.config.apiUrl.href, + }); +} + +export default api; diff --git a/app/controllers/routes/index.ts b/app/controllers/routes/index.ts new file mode 100644 index 0000000..557eb3d --- /dev/null +++ b/app/controllers/routes/index.ts @@ -0,0 +1,14 @@ +import type { FastifyInstance } from "fastify"; +import api from "./api"; +import login from "./login"; +import views from "./views"; + +async function index(fastify: FastifyInstance) { + await Promise.all([ + fastify.register(api), + fastify.register(login), + fastify.register(views), + ]); +} + +export default index; diff --git a/app/controllers/routes/login.ts b/app/controllers/routes/login.ts new file mode 100644 index 0000000..fa10fdc --- /dev/null +++ b/app/controllers/routes/login.ts @@ -0,0 +1,32 @@ +import { custom, Issuer } from "openid-client"; +import { SignJWT } from "jose"; +import type { FastifyInstance } from "fastify"; + +async function login(fastify: FastifyInstance) { + custom.setHttpOptionsDefaults(fastify.config.openid.request); + const issuer = await Issuer.discover(fastify.config.openid.issuer); + const client = new issuer.Client(fastify.config.openid.client); + + fastify.get("/login", async (request, reply) => { + const params = client.callbackParams(request.raw); + const redirect_uri = new URL("login", fastify.config.rootUrl).href; + if (!("code" in params)) { + const authorizationUrl = client.authorizationUrl({ redirect_uri }); + return reply.redirect(authorizationUrl); + } + const tokens = await client.callback(redirect_uri, params); + const userUrl = new URL(issuer.metadata.issuer); + const userinfo = await client.userinfo(tokens); + userUrl.username = userinfo.sub; + const jwt = await new SignJWT({ role: "writer" }) + .setProtectedHeader({ typ: "JWT", alg: "RS256" }) + .setExpirationTime("30days") + .setSubject(userUrl.href) + .sign(fastify.config.key); + const url = new URL(fastify.config.rootUrl); + url.hash = new URLSearchParams({ jwt }).toString(); + return reply.redirect(url.href); + }); +} + +export default login; diff --git a/app/controllers/routes/views.ts b/app/controllers/routes/views.ts new file mode 100644 index 0000000..2d64627 --- /dev/null +++ b/app/controllers/routes/views.ts @@ -0,0 +1,33 @@ +import type { FastifyInstance } from "fastify"; +import httpProxy from "@fastify/http-proxy"; +import esbuild from "esbuild"; + +async function views(fastify: FastifyInstance) { + const viewsDir = fastify.config.viewsDir; + const entryPoint = `${viewsDir}/index.ts`; + const { host, port } = await esbuild.serve( + { + host: "127.0.0.1", + servedir: viewsDir, + }, + { + bundle: true, + minify: true, + entryPoints: [entryPoint], + define: { + "import.meta.env.QUOT_API_ENDPOINT": JSON.stringify( + fastify.config.apiEndpoint + ), + }, + } + ); + + await fastify.register(httpProxy, { + upstream: `http://${host}:${port}`, + async preHandler(req) { + if (!/\.(?:html|css|js)$/.test(req.url)) req.raw.url = "/"; + }, + }); +} + +export default views; diff --git a/app/main.ts b/app/main.ts index 87d011f..c6dae4d 100644 --- a/app/main.ts +++ b/app/main.ts @@ -1,59 +1,3 @@ -import type { AddressInfo } from "node:net"; -import http from "node:http"; -import fs from "node:fs"; -import esbuild from "esbuild"; +import { App, start } from "./controllers/index"; -async function main() { - const port = Number(process.env.PORT ?? "8080"); - const apiUrl = process.env.QUOT_API_URL || "http://127.0.0.1:3000"; - const viewsDir = `${__dirname}/views`; - const htmlPath = `${viewsDir}/index.html`; - const scriptPath = `${viewsDir}/index.ts`; - - const result = await esbuild.serve( - { - host: "127.0.0.1", - servedir: viewsDir, - }, - { - bundle: true, - minify: true, - entryPoints: [scriptPath], - define: { - "import.meta.env.QUOT_API_URL": JSON.stringify( - process.env.QUOT_API_URL ?? "" - ), - }, - } - ); - - const handler: http.RequestListener = (req, res) => { - const api = req.url?.startsWith("/api/") && new URL(apiUrl); - const options = { - hostname: /**/ api ? api.hostname /* */ : result.host, - port: /* */ api ? api.port /* */ : result.port, - path: /* */ api ? req.url?.slice("/api".length) /**/ : req.url, - method: req.method, - headers: req.headers, - }; - - const proxyReq = http.request(options, (proxyRes) => { - if (api || proxyRes.statusCode === 200) { - res.writeHead(proxyRes.statusCode ?? 500, proxyRes.headers); - proxyRes.pipe(res); - } else { - res.writeHead(200, { "Content-Type": "text/html" }); - fs.createReadStream(htmlPath).pipe(res); - } - }); - - req.pipe(proxyReq); - }; - - const server = http.createServer(handler).listen(port); - const address = server.address() as AddressInfo; - - console.log(`http://0.0.0.0:${address.port}`); -} - -main(); +start(App()); diff --git a/app/package-lock.json b/app/package-lock.json index 9e73bbc..b312d04 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -9,10 +9,14 @@ "version": "0.0.0", "dependencies": { "@exampledev/new.css": "^1.1.3", + "@fastify/http-proxy": "^8.2.2", "@lexical/plain-text": "^0.3.11", "esbuild": "^0.15.5", "esbuild-register": "^3.3.3", + "fastify": "^4.5.3", + "jose": "^4.9.2", "lexical": "^0.3.11", + "openid-client": "^5.1.9", "solid-js": "^1.4.8" }, "devDependencies": { @@ -29,6 +33,57 @@ "resolved": "https://registry.npmjs.org/@exampledev/new.css/-/new.css-1.1.3.tgz", "integrity": "sha512-qhbGfqBRwUlM6MCSaJdUfjq86opNCMvM+6kVvs6S0kYhy0V8dKbe4rDMIklEJGuMc5QH5OuPjdCReu9I0tim2w==" }, + "node_modules/@fastify/ajv-compiler": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.2.0.tgz", + "integrity": "sha512-JrqgKmZoh1AJojDZk699DupQ9+tz5gSy7/w+5DrkXy5whM5IcqdV3SjG5qnOqgVJT1nPtUMDY0xYus2j6vwJiw==", + "dependencies": { + "ajv": "^8.10.0", + "ajv-formats": "^2.1.1", + "fast-uri": "^2.0.0" + } + }, + "node_modules/@fastify/deepmerge": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.1.0.tgz", + "integrity": "sha512-E8Hfdvs1bG6u0N4vN5Nty6JONUfTdOciyD5rn8KnEsLKIenvOVcr210BQR9t34PRkNyjqnMLGk3e0BsaxRdL+g==" + }, + "node_modules/@fastify/error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.0.0.tgz", + "integrity": "sha512-dPRyT40GiHRzSCll3/Jn2nPe25+E1VXc9tDwRAIKwFCxd5Np5wzgz1tmooWG3sV0qKgrBibihVoCna2ru4SEFg==" + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.1.0.tgz", + "integrity": "sha512-cTKBV2J9+u6VaKDhX7HepSfPSzw+F+TSd+k0wzifj4rG+4E5PjSFJCk19P8R6tr/72cuzgGd+mbB3jFT6lvAgw==", + "dependencies": { + "fast-json-stringify": "^5.0.0" + } + }, + "node_modules/@fastify/http-proxy": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@fastify/http-proxy/-/http-proxy-8.2.2.tgz", + "integrity": "sha512-OrStTijUytyVeWJ15EfSi3WslZeylixr6Ra/bPgM379p0HstDyJFajVkC6SaBkYgFhaVkpGvdyQA7A5fid5E1A==", + "dependencies": { + "@fastify/reply-from": "^8.0.0", + "ws": "^8.4.2" + } + }, + "node_modules/@fastify/reply-from": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@fastify/reply-from/-/reply-from-8.2.1.tgz", + "integrity": "sha512-vi0arBWSktvPEFA5cAtujrLYgt12a4HYLYE0CTgnX5YQUiEZXV5am85fHgswJtWgp5Wp/HF4cidHJuTMwLcGxQ==", + "dependencies": { + "@fastify/error": "^3.0.0", + "end-of-stream": "^1.4.4", + "fastify-plugin": "^4.0.0", + "pump": "^3.0.0", + "semver": "^7.3.7", + "tiny-lru": "^8.0.2", + "undici": "^5.5.1" + } + }, "node_modules/@lexical/clipboard": { "version": "0.3.11", "resolved": "https://registry.npmjs.org/@lexical/clipboard/-/clipboard-0.3.11.tgz", @@ -125,6 +180,108 @@ "integrity": "sha512-/YP55EMK2341JkODUb8DM9O0x1SIz2aBvyF33Uf1c76St3VpsMXEIW0nxuKkq/5cxnbz0RD9cfwNZHEAZQD3ag==", "dev": true }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" + }, + "node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/avvio": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.0.tgz", + "integrity": "sha512-bbCQdg7bpEv6kGH41RO/3B2/GMMmJSo2iBK+X8AWN9mujtfUipMDfIjsgHCfpnKqoGEQrrmCDKSa5OQ19+fDmg==", + "dependencies": { + "archy": "^1.0.0", + "debug": "^4.0.0", + "fastq": "^1.6.1" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/esbuild": { "version": "0.15.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.5.tgz", @@ -183,16 +340,397 @@ "esbuild": ">=0.12 <1" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-json-stringify": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.2.0.tgz", + "integrity": "sha512-u5jtrcAK9RINW15iuDKnsuuhqmqre4AmDMp3crRTjUMdAuHMpQUt3IfoMm5wlJm59b74PcajqOl3SjgnC5FPmw==", + "dependencies": { + "@fastify/deepmerge": "^1.0.0", + "ajv": "^8.10.0", + "ajv-formats": "^2.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^2.1.0", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-redact": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.2.tgz", + "integrity": "sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-uri": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.1.0.tgz", + "integrity": "sha512-qKRta6N7BWEFVlyonVY/V+BMLgFqktCUV0QjT259ekAIlbVrMaFnFLxJ4s/JPl4tou56S1BzPufI60bLe29fHA==" + }, + "node_modules/fastify": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.5.3.tgz", + "integrity": "sha512-Q8Zvkmg7GnioMCDX1jT2Q7iRqjywlnDZ1735D2Ipf7ashCM/3/bqPKv2Jo1ZF2iDExct2eP1C/tdhcj0GG/OuQ==", + "dependencies": { + "@fastify/ajv-compiler": "^3.1.1", + "@fastify/error": "^3.0.0", + "@fastify/fast-json-stringify-compiler": "^4.0.0", + "abstract-logging": "^2.0.1", + "avvio": "^8.1.3", + "find-my-way": "^7.0.0", + "light-my-request": "^5.5.1", + "pino": "^8.0.0", + "process-warning": "^2.0.0", + "proxy-addr": "^2.0.7", + "rfdc": "^1.3.0", + "secure-json-parse": "^2.4.0", + "semver": "^7.3.7", + "tiny-lru": "^8.0.2" + } + }, + "node_modules/fastify-plugin": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.2.1.tgz", + "integrity": "sha512-dlGKiwLzRBKkEf5J5ho0uAD/Jdv8GQVUbriB3tAX3ehRUXE4gTV3lRd5inEg9li1aLzb0EGj8y2K4/8g1TN06g==" + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/find-my-way": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.0.1.tgz", + "integrity": "sha512-w05SaOPg54KqBof/RDA+75n1R48V7ZZNPL3nR17jJJs5dgZpR3ivfrMWOyx7BVFQgCLhYRG05hfgFCohYvSUXA==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "safe-regex2": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/jose": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.9.2.tgz", + "integrity": "sha512-EqKvu2PqJCD3Jrg3PvcYZVS7D21qMVLSYMDAFcOdGUEOpJSLNtJO7NjLANvu3SYHVl6pdP2ff7ve6EZW2nX7Nw==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/lexical": { "version": "0.3.11", "resolved": "https://registry.npmjs.org/lexical/-/lexical-0.3.11.tgz", "integrity": "sha512-HZvQ2T3g0jWBX6MC/A0HY1N7NMvR+FrmTfR4vn6WnoCg56UPlkgIX3GKa6rCyGOAnOtykXRPJ831JWEKJUHalQ==" }, + "node_modules/light-my-request": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.5.1.tgz", + "integrity": "sha512-Zd4oZjF7axSyc5rYQsbB0qsgY4LFFviZSbEywxf7Vi5UE3y3c7tYF/GeheQjBNYY+pQ55BF8UGGJTjneoxOS1w==", + "dependencies": { + "cookie": "^0.5.0", + "process-warning": "^2.0.0", + "set-cookie-parser": "^2.4.1" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/oidc-token-hash": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz", + "integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==", + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", + "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/openid-client": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.1.9.tgz", + "integrity": "sha512-o/11Xos2fRPpK1zQrPfSIhIusFrAkqGSPwkD0UlUB+CCuRzd7zrrBJwIjgnVv3VUSif9ZGXh2d3GSJNH2Koh5g==", + "dependencies": { + "jose": "^4.1.4", + "lru-cache": "^6.0.0", + "object-hash": "^2.0.1", + "oidc-token-hash": "^5.0.1" + }, + "engines": { + "node": "^12.19.0 || ^14.15.0 || ^16.13.0" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/pino": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.4.2.tgz", + "integrity": "sha512-PlXDeGhJZfAuVay+wtlS02s5j8uisQveZExYdAm9MwwxUQSz9R7Q78XtjM2tTa4sa5KJmygimZjZxXXuHgV6ew==", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "v1.0.0", + "pino-std-serializers": "^6.0.0", + "process-warning": "^2.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.1.0", + "thread-stream": "^2.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz", + "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==", + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.0.0.tgz", + "integrity": "sha512-mMMOwSKrmyl+Y12Ri2xhH1lbzQxwwpuru9VjyJpgFIH4asSj88F2csdMwN6+M5g1Ll4rmsYghHLQJw81tgZ7LQ==" + }, + "node_modules/process-warning": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.0.0.tgz", + "integrity": "sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, + "node_modules/readable-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.1.0.tgz", + "integrity": "sha512-sVisi3+P2lJ2t0BPbpK629j8wRW06yKGJUcaLAGXPAUhyUxVJm7VsCTit1PFgT4JHUDMrGNR+ZjSKpzGaRF3zw==", + "dependencies": { + "abort-controller": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, + "node_modules/safe-regex2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", + "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", + "dependencies": { + "ret": "~0.2.0" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", + "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/secure-json-parse": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.5.0.tgz", + "integrity": "sha512-ZQruFgZnIWH+WyO9t5rWt4ZEGqCKPwhiw+YbzTwpmT9elgLrLcfuyUiSnwwjUiVy9r4VM3urtbNF1xmEh9IL2w==" + }, + "node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", + "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==" + }, "node_modules/solid-js": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.4.8.tgz", "integrity": "sha512-XErZdnnYYXF7OwGSUAPcua2y5/ELB/c53zFCpWiEGqxTNoH1iQghzI8EsHJXk06sNn+Z/TGhb8bPDNNGSgimag==" }, + "node_modules/sonic-boom": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.2.0.tgz", + "integrity": "sha512-SbbZ+Kqj/XIunvIAgUZRlqd6CGQYq71tRRbXR92Za8J/R3Yh4Av+TWENiSiEgnlwckYLyP0YZQWVfyNC0dzLaA==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/thread-stream": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.1.0.tgz", + "integrity": "sha512-5+Pf2Ya31CsZyIPYYkhINzdTZ3guL+jHq7D8lkBybgGcSQIKDbid3NJku3SpCKeE/gACWAccDA/rH2B6doC5aA==", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/tiny-lru": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-8.0.2.tgz", + "integrity": "sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==", + "engines": { + "node": ">=6" + } + }, "node_modules/typescript": { "version": "4.7.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", @@ -205,6 +743,52 @@ "engines": { "node": ">=4.2.0" } + }, + "node_modules/undici": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.10.0.tgz", + "integrity": "sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g==", + "engines": { + "node": ">=12.18" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/ws": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", + "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } }, "dependencies": { @@ -213,6 +797,57 @@ "resolved": "https://registry.npmjs.org/@exampledev/new.css/-/new.css-1.1.3.tgz", "integrity": "sha512-qhbGfqBRwUlM6MCSaJdUfjq86opNCMvM+6kVvs6S0kYhy0V8dKbe4rDMIklEJGuMc5QH5OuPjdCReu9I0tim2w==" }, + "@fastify/ajv-compiler": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.2.0.tgz", + "integrity": "sha512-JrqgKmZoh1AJojDZk699DupQ9+tz5gSy7/w+5DrkXy5whM5IcqdV3SjG5qnOqgVJT1nPtUMDY0xYus2j6vwJiw==", + "requires": { + "ajv": "^8.10.0", + "ajv-formats": "^2.1.1", + "fast-uri": "^2.0.0" + } + }, + "@fastify/deepmerge": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.1.0.tgz", + "integrity": "sha512-E8Hfdvs1bG6u0N4vN5Nty6JONUfTdOciyD5rn8KnEsLKIenvOVcr210BQR9t34PRkNyjqnMLGk3e0BsaxRdL+g==" + }, + "@fastify/error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.0.0.tgz", + "integrity": "sha512-dPRyT40GiHRzSCll3/Jn2nPe25+E1VXc9tDwRAIKwFCxd5Np5wzgz1tmooWG3sV0qKgrBibihVoCna2ru4SEFg==" + }, + "@fastify/fast-json-stringify-compiler": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.1.0.tgz", + "integrity": "sha512-cTKBV2J9+u6VaKDhX7HepSfPSzw+F+TSd+k0wzifj4rG+4E5PjSFJCk19P8R6tr/72cuzgGd+mbB3jFT6lvAgw==", + "requires": { + "fast-json-stringify": "^5.0.0" + } + }, + "@fastify/http-proxy": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@fastify/http-proxy/-/http-proxy-8.2.2.tgz", + "integrity": "sha512-OrStTijUytyVeWJ15EfSi3WslZeylixr6Ra/bPgM379p0HstDyJFajVkC6SaBkYgFhaVkpGvdyQA7A5fid5E1A==", + "requires": { + "@fastify/reply-from": "^8.0.0", + "ws": "^8.4.2" + } + }, + "@fastify/reply-from": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@fastify/reply-from/-/reply-from-8.2.1.tgz", + "integrity": "sha512-vi0arBWSktvPEFA5cAtujrLYgt12a4HYLYE0CTgnX5YQUiEZXV5am85fHgswJtWgp5Wp/HF4cidHJuTMwLcGxQ==", + "requires": { + "@fastify/error": "^3.0.0", + "end-of-stream": "^1.4.4", + "fastify-plugin": "^4.0.0", + "pump": "^3.0.0", + "semver": "^7.3.7", + "tiny-lru": "^8.0.2", + "undici": "^5.5.1" + } + }, "@lexical/clipboard": { "version": "0.3.11", "resolved": "https://registry.npmjs.org/@lexical/clipboard/-/clipboard-0.3.11.tgz", @@ -287,6 +922,79 @@ "integrity": "sha512-/YP55EMK2341JkODUb8DM9O0x1SIz2aBvyF33Uf1c76St3VpsMXEIW0nxuKkq/5cxnbz0RD9cfwNZHEAZQD3ag==", "dev": true }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" + }, + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" + }, + "atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" + }, + "avvio": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.0.tgz", + "integrity": "sha512-bbCQdg7bpEv6kGH41RO/3B2/GMMmJSo2iBK+X8AWN9mujtfUipMDfIjsgHCfpnKqoGEQrrmCDKSa5OQ19+fDmg==", + "requires": { + "archy": "^1.0.0", + "debug": "^4.0.0", + "fastq": "^1.6.1" + } + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, "esbuild": { "version": "0.15.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.5.tgz", @@ -327,21 +1035,358 @@ "integrity": "sha512-eFHOkutgIMJY5gc8LUp/7c+LLlDqzNi9T6AwCZ2WKKl3HmT+5ef3ZRyPPxDOynInML0fgaC50yszPKfPnjC0NQ==", "requires": {} }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stringify": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.2.0.tgz", + "integrity": "sha512-u5jtrcAK9RINW15iuDKnsuuhqmqre4AmDMp3crRTjUMdAuHMpQUt3IfoMm5wlJm59b74PcajqOl3SjgnC5FPmw==", + "requires": { + "@fastify/deepmerge": "^1.0.0", + "ajv": "^8.10.0", + "ajv-formats": "^2.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^2.1.0", + "rfdc": "^1.2.0" + } + }, + "fast-redact": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.2.tgz", + "integrity": "sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==" + }, + "fast-uri": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.1.0.tgz", + "integrity": "sha512-qKRta6N7BWEFVlyonVY/V+BMLgFqktCUV0QjT259ekAIlbVrMaFnFLxJ4s/JPl4tou56S1BzPufI60bLe29fHA==" + }, + "fastify": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.5.3.tgz", + "integrity": "sha512-Q8Zvkmg7GnioMCDX1jT2Q7iRqjywlnDZ1735D2Ipf7ashCM/3/bqPKv2Jo1ZF2iDExct2eP1C/tdhcj0GG/OuQ==", + "requires": { + "@fastify/ajv-compiler": "^3.1.1", + "@fastify/error": "^3.0.0", + "@fastify/fast-json-stringify-compiler": "^4.0.0", + "abstract-logging": "^2.0.1", + "avvio": "^8.1.3", + "find-my-way": "^7.0.0", + "light-my-request": "^5.5.1", + "pino": "^8.0.0", + "process-warning": "^2.0.0", + "proxy-addr": "^2.0.7", + "rfdc": "^1.3.0", + "secure-json-parse": "^2.4.0", + "semver": "^7.3.7", + "tiny-lru": "^8.0.2" + } + }, + "fastify-plugin": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.2.1.tgz", + "integrity": "sha512-dlGKiwLzRBKkEf5J5ho0uAD/Jdv8GQVUbriB3tAX3ehRUXE4gTV3lRd5inEg9li1aLzb0EGj8y2K4/8g1TN06g==" + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "requires": { + "reusify": "^1.0.4" + } + }, + "find-my-way": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.0.1.tgz", + "integrity": "sha512-w05SaOPg54KqBof/RDA+75n1R48V7ZZNPL3nR17jJJs5dgZpR3ivfrMWOyx7BVFQgCLhYRG05hfgFCohYvSUXA==", + "requires": { + "fast-deep-equal": "^3.1.3", + "safe-regex2": "^2.0.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "jose": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.9.2.tgz", + "integrity": "sha512-EqKvu2PqJCD3Jrg3PvcYZVS7D21qMVLSYMDAFcOdGUEOpJSLNtJO7NjLANvu3SYHVl6pdP2ff7ve6EZW2nX7Nw==" + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "lexical": { "version": "0.3.11", "resolved": "https://registry.npmjs.org/lexical/-/lexical-0.3.11.tgz", "integrity": "sha512-HZvQ2T3g0jWBX6MC/A0HY1N7NMvR+FrmTfR4vn6WnoCg56UPlkgIX3GKa6rCyGOAnOtykXRPJ831JWEKJUHalQ==" }, + "light-my-request": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.5.1.tgz", + "integrity": "sha512-Zd4oZjF7axSyc5rYQsbB0qsgY4LFFviZSbEywxf7Vi5UE3y3c7tYF/GeheQjBNYY+pQ55BF8UGGJTjneoxOS1w==", + "requires": { + "cookie": "^0.5.0", + "process-warning": "^2.0.0", + "set-cookie-parser": "^2.4.1" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" + }, + "oidc-token-hash": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz", + "integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==" + }, + "on-exit-leak-free": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", + "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "openid-client": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.1.9.tgz", + "integrity": "sha512-o/11Xos2fRPpK1zQrPfSIhIusFrAkqGSPwkD0UlUB+CCuRzd7zrrBJwIjgnVv3VUSif9ZGXh2d3GSJNH2Koh5g==", + "requires": { + "jose": "^4.1.4", + "lru-cache": "^6.0.0", + "object-hash": "^2.0.1", + "oidc-token-hash": "^5.0.1" + } + }, + "pino": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.4.2.tgz", + "integrity": "sha512-PlXDeGhJZfAuVay+wtlS02s5j8uisQveZExYdAm9MwwxUQSz9R7Q78XtjM2tTa4sa5KJmygimZjZxXXuHgV6ew==", + "requires": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "v1.0.0", + "pino-std-serializers": "^6.0.0", + "process-warning": "^2.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.1.0", + "thread-stream": "^2.0.0" + } + }, + "pino-abstract-transport": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz", + "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==", + "requires": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "pino-std-serializers": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.0.0.tgz", + "integrity": "sha512-mMMOwSKrmyl+Y12Ri2xhH1lbzQxwwpuru9VjyJpgFIH4asSj88F2csdMwN6+M5g1Ll4rmsYghHLQJw81tgZ7LQ==" + }, + "process-warning": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.0.0.tgz", + "integrity": "sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==" + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, + "readable-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.1.0.tgz", + "integrity": "sha512-sVisi3+P2lJ2t0BPbpK629j8wRW06yKGJUcaLAGXPAUhyUxVJm7VsCTit1PFgT4JHUDMrGNR+ZjSKpzGaRF3zw==", + "requires": { + "abort-controller": "^3.0.0" + } + }, + "real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==" + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, + "safe-regex2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", + "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", + "requires": { + "ret": "~0.2.0" + } + }, + "safe-stable-stringify": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", + "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==" + }, + "secure-json-parse": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.5.0.tgz", + "integrity": "sha512-ZQruFgZnIWH+WyO9t5rWt4ZEGqCKPwhiw+YbzTwpmT9elgLrLcfuyUiSnwwjUiVy9r4VM3urtbNF1xmEh9IL2w==" + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "set-cookie-parser": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", + "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==" + }, "solid-js": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.4.8.tgz", "integrity": "sha512-XErZdnnYYXF7OwGSUAPcua2y5/ELB/c53zFCpWiEGqxTNoH1iQghzI8EsHJXk06sNn+Z/TGhb8bPDNNGSgimag==" }, + "sonic-boom": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.2.0.tgz", + "integrity": "sha512-SbbZ+Kqj/XIunvIAgUZRlqd6CGQYq71tRRbXR92Za8J/R3Yh4Av+TWENiSiEgnlwckYLyP0YZQWVfyNC0dzLaA==", + "requires": { + "atomic-sleep": "^1.0.0" + } + }, + "split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" + }, + "thread-stream": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.1.0.tgz", + "integrity": "sha512-5+Pf2Ya31CsZyIPYYkhINzdTZ3guL+jHq7D8lkBybgGcSQIKDbid3NJku3SpCKeE/gACWAccDA/rH2B6doC5aA==", + "requires": { + "real-require": "^0.2.0" + } + }, + "tiny-lru": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-8.0.2.tgz", + "integrity": "sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==" + }, "typescript": { "version": "4.7.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "dev": true + }, + "undici": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.10.0.tgz", + "integrity": "sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g==" + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "ws": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", + "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "requires": {} + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/app/package.json b/app/package.json index ad52c5c..119ea0a 100644 --- a/app/package.json +++ b/app/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "test": "tsc --noEmit", - "start": "node -r esbuild-register main.ts" + "start": "TS_NODE_DEV=true node -r esbuild-register main.ts" }, "engines": { "node": "^18.7.0" @@ -12,10 +12,14 @@ "packageManager": "npm@8.19.0", "dependencies": { "@exampledev/new.css": "^1.1.3", + "@fastify/http-proxy": "^8.2.2", "@lexical/plain-text": "^0.3.11", "esbuild": "^0.15.5", "esbuild-register": "^3.3.3", + "fastify": "^4.5.3", + "jose": "^4.9.2", "lexical": "^0.3.11", + "openid-client": "^5.1.9", "solid-js": "^1.4.8" }, "devDependencies": { diff --git a/app/views/app.tsx b/app/views/app.tsx index fb2cd1c..dcb3bca 100644 --- a/app/views/app.tsx +++ b/app/views/app.tsx @@ -3,11 +3,32 @@ import { createSignal } from "solid-js"; import Index from "./pages/index"; import Page from "./pages/page"; import random from "./helpers/random"; +import { decodeJwt } from "jose"; const routes = { "/": Index, }; +async function updateUser(jwt: string): Promise { + const decoded = decodeJwt(jwt); + const id = decoded.sub ?? ""; + if (!id) return false; + const res = await fetch( + `${import.meta.env.QUOT_API_ENDPOINT}/users?id=eq.${encodeURIComponent( + id + )}`, + { + method: "PUT", + headers: { + authorization: `Bearer ${jwt}`, + "content-type": "application/json", + }, + body: JSON.stringify({ id }), + } + ); + return res.ok; +} + export default () => { const [pathname, setPathname] = createSignal( document.location.pathname as keyof typeof routes @@ -29,6 +50,18 @@ export default () => { setPathname(document.location.pathname as keyof typeof routes); }); + if (window.location.hash) { + const params = new URLSearchParams(window.location.hash.slice(1)); + const jwt = params.get("jwt"); + if (jwt) { + window.history.replaceState({}, "", "."); + window.localStorage.setItem("jwt", jwt); + updateUser(jwt); + } + } + + const authenticated = Boolean(window.localStorage.getItem("jwt")); + return () => ( <>
@@ -36,7 +69,7 @@ export default () => { Quot
{routes[pathname()] ?? ( diff --git a/app/views/env.d.ts b/app/views/env.d.ts index 5e56e54..f9719bb 100644 --- a/app/views/env.d.ts +++ b/app/views/env.d.ts @@ -1,5 +1,5 @@ interface ImportMeta { env: { - QUOT_API_URL: string; + QUOT_API_ENDPOINT: string; }; } diff --git a/app/views/pages/index.tsx b/app/views/pages/index.tsx index 63f3ff7..125e038 100644 --- a/app/views/pages/index.tsx +++ b/app/views/pages/index.tsx @@ -4,8 +4,10 @@ import Cards from "../components/cards"; import Card from "../components/card"; async function fetchPages(): Promise { + const jwt = window.localStorage.getItem("jwt"); const res = await fetch( - `${import.meta.env.QUOT_API_URL}/api/pages?order=updated.desc` + `${import.meta.env.QUOT_API_ENDPOINT}/pages?order=updated.desc`, + { headers: jwt ? { authorization: `Bearer ${jwt}` } : {} } ); const data = (await res.json()) as Pages.Response; return data; diff --git a/app/views/pages/page.tsx b/app/views/pages/page.tsx index d4bac2b..8180afa 100644 --- a/app/views/pages/page.tsx +++ b/app/views/pages/page.tsx @@ -10,11 +10,16 @@ async function updatePage( id: number, content: Pages.RequestContentPage ): Promise { + const jwt = window.localStorage.getItem("jwt"); + if (!jwt) return false; const res = await fetch( - `${import.meta.env.QUOT_API_URL}/api/pages?id=eq.${id}`, + `${import.meta.env.QUOT_API_ENDPOINT}/pages?id=eq.${id}`, { method: "PUT", - headers: { "content-type": "application/json" }, + headers: { + authorization: `Bearer ${jwt}`, + "content-type": "application/json", + }, body: JSON.stringify(content), } ); @@ -22,16 +27,23 @@ async function updatePage( } async function deletePage(id: number): Promise { + const jwt = window.localStorage.getItem("jwt"); + if (!jwt) return false; const res = await fetch( - `${import.meta.env.QUOT_API_URL}/api/pages?id=eq.${id}`, - { method: "DELETE" } + `${import.meta.env.QUOT_API_ENDPOINT}/pages?id=eq.${id}`, + { + method: "DELETE", + headers: { authorization: `Bearer ${jwt}` }, + } ); return res.ok; } async function fetchPage(id: number): Promise { + const jwt = window.localStorage.getItem("jwt"); const res = await fetch( - `${import.meta.env.QUOT_API_URL}/api/pages?id=eq.${id}` + `${import.meta.env.QUOT_API_ENDPOINT}/pages?id=eq.${id}`, + { headers: jwt ? { authorization: `Bearer ${jwt}` } : {} } ); const data = (await res.json()) as Pages.Response; return data[0]!; diff --git a/compose.yml b/compose.yml index 07bf0e3..a7795af 100644 --- a/compose.yml +++ b/compose.yml @@ -5,10 +5,30 @@ services: restart: unless-stopped ports: ["8080:8080"] environment: + # https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata + QUOT_OPENID_ISSUER: ${QUOT_OPENID_ISSUER:?} + # https://www.rfc-editor.org/rfc/rfc6749#section-2.3.1 + QUOT_OPENID_CLIENT_ID: ${QUOT_OPENID_CLIENT_ID:?} + QUOT_OPENID_CLIENT_SECRET: ${QUOT_OPENID_CLIENT_SECRET:?} + QUOT_JWK: ${QUOT_JWK:?} # https://mkjwk.org DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@/postgres?host=/var/run/postgresql volumes: - postgres_socket:/var/run/postgresql depends_on: [db] + api: + profiles: [dev] + image: kou029w/quot + build: "." + restart: unless-stopped + ports: ["3000:3000"] + environment: + QUOT_JWK: ${QUOT_JWK:?} # https://mkjwk.org + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@/postgres?host=/var/run/postgresql + volumes: + - postgres_socket:/var/run/postgresql + depends_on: + db: + condition: service_healthy dbmate: profiles: [dev] image: amacneil/dbmate:1.15@sha256:8fb25de3fce073e39eb3f9411af0410d0e26cc6d120544a7510b964e218abc27 diff --git a/db/migrations/0_init.sql b/db/migrations/0_init.sql index b6b6c0f..a17d498 100644 --- a/db/migrations/0_init.sql +++ b/db/migrations/0_init.sql @@ -1,6 +1,8 @@ -- migrate:up -CREATE ROLE anon; -ALTER ROLE anon SET statement_timeout = '1s'; +CREATE ROLE guest; +ALTER ROLE guest SET statement_timeout = '1s'; +CREATE ROLE writer; +ALTER ROLE writer SET statement_timeout = '1s'; CREATE FUNCTION update_timestamp() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN @@ -9,20 +11,52 @@ CREATE FUNCTION update_timestamp() RETURNS trigger LANGUAGE plpgsql AS $$ END $$; -CREATE TABLE pages ( - id SERIAL PRIMARY KEY, - title TEXT NOT NULL UNIQUE, - text TEXT NOT NULL, +CREATE TABLE users ( + id TEXT PRIMARY KEY DEFAULT current_setting('request.jwt.claims', true)::json ->> 'sub'::text, created TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ); -GRANT ALL ON pages TO anon; +CREATE TRIGGER users_updated BEFORE UPDATE ON users FOR EACH ROW + EXECUTE PROCEDURE update_timestamp(); + +GRANT ALL (id) ON users TO writer; +GRANT SELECT, DELETE ON users TO writer; +ALTER TABLE users ENABLE ROW LEVEL SECURITY; +CREATE POLICY users_policy ON users USING ( + id = current_setting('request.jwt.claims', true)::json ->> 'sub'::text +); + +CREATE TABLE pages ( + id SERIAL PRIMARY KEY, + user_id TEXT REFERENCES users ON DELETE CASCADE DEFAULT current_setting('request.jwt.claims', true)::json ->> 'sub'::text, + title TEXT NOT NULL UNIQUE, + text TEXT NOT NULL, + created TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + published TIMESTAMPTZ +); CREATE TRIGGER pages_updated BEFORE UPDATE ON pages FOR EACH ROW EXECUTE PROCEDURE update_timestamp(); +ALTER TABLE pages ENABLE ROW LEVEL SECURITY; + +GRANT SELECT ON pages TO guest; +CREATE POLICY pages_guest_read_policy ON pages TO guest USING ( + published <= CURRENT_TIMESTAMP +); + +GRANT SELECT, DELETE ON pages TO writer; +GRANT ALL (id, title, text, published) ON pages TO writer; +GRANT ALL ON SEQUENCE pages_id_seq TO writer; +CREATE POLICY pages_write_policy ON pages TO writer USING ( + user_id = current_setting('request.jwt.claims', true)::json ->> 'sub'::text +); + -- migrate:down +DROP TABLE users; DROP TABLE pages; DROP FUNCTION update_timestamp(); -DROP ROLE anon; +DROP ROLE guest; +DROP ROLE writer; diff --git a/db/schema.sql b/db/schema.sql index af08767..33f7499 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -33,10 +33,12 @@ SET default_table_access_method = heap; CREATE TABLE public.pages ( id integer NOT NULL, + user_id text DEFAULT ((current_setting('request.jwt.claims'::text, true))::json ->> 'sub'::text), title text NOT NULL, text text NOT NULL, created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - updated timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL + updated timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + published timestamp with time zone ); @@ -69,6 +71,17 @@ CREATE TABLE public.schema_migrations ( ); +-- +-- Name: users; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.users ( + id text DEFAULT ((current_setting('request.jwt.claims'::text, true))::json ->> 'sub'::text) NOT NULL, + created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL +); + + -- -- Name: pages id; Type: DEFAULT; Schema: public; Owner: - -- @@ -100,6 +113,14 @@ ALTER TABLE ONLY public.schema_migrations ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); +-- +-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT users_pkey PRIMARY KEY (id); + + -- -- Name: pages pages_updated; Type: TRIGGER; Schema: public; Owner: - -- @@ -107,6 +128,54 @@ ALTER TABLE ONLY public.schema_migrations CREATE TRIGGER pages_updated BEFORE UPDATE ON public.pages FOR EACH ROW EXECUTE FUNCTION public.update_timestamp(); +-- +-- Name: users users_updated; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER users_updated BEFORE UPDATE ON public.users FOR EACH ROW EXECUTE FUNCTION public.update_timestamp(); + + +-- +-- Name: pages pages_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.pages + ADD CONSTRAINT pages_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: pages; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.pages ENABLE ROW LEVEL SECURITY; + +-- +-- Name: pages pages_guest_read_policy; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY pages_guest_read_policy ON public.pages TO guest USING ((published <= CURRENT_TIMESTAMP)); + + +-- +-- Name: pages pages_write_policy; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY pages_write_policy ON public.pages TO writer USING ((user_id = ((current_setting('request.jwt.claims'::text, true))::json ->> 'sub'::text))); + + +-- +-- Name: users; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.users ENABLE ROW LEVEL SECURITY; + +-- +-- Name: users users_policy; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY users_policy ON public.users USING ((id = ((current_setting('request.jwt.claims'::text, true))::json ->> 'sub'::text))); + + -- -- PostgreSQL database dump complete -- diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 2b7562f..383659f 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -3,5 +3,6 @@ set -e dbmate --wait up export PGRST_DB_URI="${PGRST_DB_URI:-${DATABASE_URL}}" export PGRST_DB_SCHEMA="${PGRST_DB_SCHEMA:-public}" -export PGRST_DB_ANON_ROLE="${PGRST_DB_ANON_ROLE:-anon}" +export PGRST_DB_ANON_ROLE="${PGRST_DB_ANON_ROLE:-guest}" +export PGRST_JWT_SECRET="${QUOT_JWK}" exec "$@"