From b9bd3c15a6c76891588863641eef7cab06f4ac7a Mon Sep 17 00:00:00 2001 From: dejan-crocoder Date: Thu, 1 Feb 2024 12:53:42 +0100 Subject: [PATCH 1/3] feat: app-htmx --- apps/app-htmx/build.mjs | 29 + apps/app-htmx/package.json | 26 + apps/app-htmx/src/app-config.ts | 35 + apps/app-htmx/src/context/htmx.context.ts | 7 + apps/app-htmx/src/context/page.context.ts | 5 + .../src/context/tenant-list.context.ts | 17 + .../src/functions/fetch-repository.ts | 42 ++ .../app-htmx/src/functions/get-crawl-stats.ts | 53 ++ .../src/functions/get-repositories.ts | 14 + apps/app-htmx/src/index.ts | 38 ++ apps/app-htmx/src/pages/page.home.ts | 59 ++ apps/app-htmx/src/pages/page.sign-in.ts | 17 + .../app-htmx/src/pages/redirect-to-sign-in.ts | 3 + apps/app-htmx/src/pages/repository/extract.ts | 72 +++ .../app-htmx/src/pages/repository/register.ts | 57 ++ apps/app-htmx/src/pages/start-transform.ts | 39 ++ apps/app-htmx/src/plugins/auth.plugin.ts | 13 + .../src/views/component.crawl-stats.html | 22 + apps/app-htmx/src/views/component.log.html | 5 + .../src/views/component.repository.html | 40 ++ .../src/views/layout.app.boosted.html | 3 + .../src/views/layout.app.default.html | 56 ++ apps/app-htmx/src/views/layout.app.html | 5 + apps/app-htmx/src/views/page.home.html | 37 ++ apps/app-htmx/src/views/page.sign-in.html | 24 + apps/app-htmx/src/views/root.html | 56 ++ apps/app-htmx/tsconfig.json | 11 + package-lock.json | 607 +++++++++++++++++- 28 files changed, 1389 insertions(+), 3 deletions(-) create mode 100644 apps/app-htmx/build.mjs create mode 100644 apps/app-htmx/package.json create mode 100644 apps/app-htmx/src/app-config.ts create mode 100644 apps/app-htmx/src/context/htmx.context.ts create mode 100644 apps/app-htmx/src/context/page.context.ts create mode 100644 apps/app-htmx/src/context/tenant-list.context.ts create mode 100644 apps/app-htmx/src/functions/fetch-repository.ts create mode 100644 apps/app-htmx/src/functions/get-crawl-stats.ts create mode 100644 apps/app-htmx/src/functions/get-repositories.ts create mode 100644 apps/app-htmx/src/index.ts create mode 100644 apps/app-htmx/src/pages/page.home.ts create mode 100644 apps/app-htmx/src/pages/page.sign-in.ts create mode 100644 apps/app-htmx/src/pages/redirect-to-sign-in.ts create mode 100644 apps/app-htmx/src/pages/repository/extract.ts create mode 100644 apps/app-htmx/src/pages/repository/register.ts create mode 100644 apps/app-htmx/src/pages/start-transform.ts create mode 100644 apps/app-htmx/src/plugins/auth.plugin.ts create mode 100644 apps/app-htmx/src/views/component.crawl-stats.html create mode 100644 apps/app-htmx/src/views/component.log.html create mode 100644 apps/app-htmx/src/views/component.repository.html create mode 100644 apps/app-htmx/src/views/layout.app.boosted.html create mode 100644 apps/app-htmx/src/views/layout.app.default.html create mode 100644 apps/app-htmx/src/views/layout.app.html create mode 100644 apps/app-htmx/src/views/page.home.html create mode 100644 apps/app-htmx/src/views/page.sign-in.html create mode 100644 apps/app-htmx/src/views/root.html create mode 100644 apps/app-htmx/tsconfig.json diff --git a/apps/app-htmx/build.mjs b/apps/app-htmx/build.mjs new file mode 100644 index 000000000..8018e8e8d --- /dev/null +++ b/apps/app-htmx/build.mjs @@ -0,0 +1,29 @@ +import * as esbuild from "esbuild"; + +await esbuild.build({ + keepNames: true, + bundle: true, + platform: "node", + format: "esm", + target: "esnext", + metafile: true, + outdir: "out", + entryPoints: ["src/index.ts"], + mainFields: ["module", "main"], + external: [ + '@clerk/fastify', + '@fastify/formbody', + '@fastify/view', + 'fastify', + 'nunjucks', + ], + banner: { + js: [ + `import { createRequire as topLevelCreateRequire } from 'module';`, + `const require = topLevelCreateRequire(import.meta.url);`, + `import { fileURLToPath as topLevelFileUrlToPath, URL as topLevelURL } from "url"`, + `const __dirname = topLevelFileUrlToPath(new topLevelURL(".", import.meta.url))`, + ].join("\n"), + }, + +}); \ No newline at end of file diff --git a/apps/app-htmx/package.json b/apps/app-htmx/package.json new file mode 100644 index 000000000..d023b3272 --- /dev/null +++ b/apps/app-htmx/package.json @@ -0,0 +1,26 @@ +{ + "name": "@acme/app-htmx", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "start": "esbuild src/index.ts --bundle --platform=node | npm run with-env node", + "dev": "node build.mjs && npm run with-env node ./out/index.js", + "with-env": "dotenv -e ../../.env --" + }, + "devDependencies": { + "@types/nunjucks": "3.2.6", + "dotenv-cli": "^7.3.0", + "esbuild": "^0.19.9" + }, + "dependencies": { + "@acme/source-control": "*", + "@acme/super-schema": "*", + "@clerk/fastify": "0.6.30", + "@fastify/formbody": "7.4.0", + "@fastify/view": "8.2.0", + "date-fns": "3.3.1", + "fastify": "4.25.2", + "nunjucks": "3.2.4" + } +} diff --git a/apps/app-htmx/src/app-config.ts b/apps/app-htmx/src/app-config.ts new file mode 100644 index 000000000..9ec9cbef8 --- /dev/null +++ b/apps/app-htmx/src/app-config.ts @@ -0,0 +1,35 @@ +import { z } from "zod" + +const ENVSchema = z.object({ + SUPER_DATABASE_URL: z.string(), + SUPER_DATABASE_AUTH_TOKEN: z.string().optional(), + TENANT_DATABASE_AUTH_TOKEN: z.string().optional(), + + // TODO: remove after removing next + NEXT_PUBLIC_EXTRACT_API_URL: z.string(), + NEXT_PUBLIC_TRANSFORM_API_URL: z.string(), + + CLERK_PUBLISHABLE_KEY: z.string(), + CLERK_SECRET_KEY: z.string(), + CLERK_DOMAIN: z.string(), +}); + +const parsedEnv = ENVSchema.safeParse(process.env); +if (!parsedEnv.success) throw new Error(`Invalid environment: ${parsedEnv.error}`); + +export const AppConfig = { + superDatabase: { + url: parsedEnv.data.SUPER_DATABASE_URL, + authToken: parsedEnv.data.SUPER_DATABASE_AUTH_TOKEN, + }, + tenantDatabaseAuthToken: parsedEnv.data.TENANT_DATABASE_AUTH_TOKEN, + apis: { + extractStart: parsedEnv.data.NEXT_PUBLIC_EXTRACT_API_URL, + transformStart: parsedEnv.data.NEXT_PUBLIC_TRANSFORM_API_URL, + }, + clerk: { + domain: parsedEnv.data.CLERK_DOMAIN, + publishableKey: parsedEnv.data.CLERK_PUBLISHABLE_KEY, + secretKey: "", // should be left in ENV + } +} \ No newline at end of file diff --git a/apps/app-htmx/src/context/htmx.context.ts b/apps/app-htmx/src/context/htmx.context.ts new file mode 100644 index 000000000..3c767c685 --- /dev/null +++ b/apps/app-htmx/src/context/htmx.context.ts @@ -0,0 +1,7 @@ +import type { IncomingHttpHeaders } from "http"; + +export const htmxContext = (headers: IncomingHttpHeaders) => ({ + htmx: { + boosted: 'hx-boosted' in headers, + } +}); \ No newline at end of file diff --git a/apps/app-htmx/src/context/page.context.ts b/apps/app-htmx/src/context/page.context.ts new file mode 100644 index 000000000..9cf649b94 --- /dev/null +++ b/apps/app-htmx/src/context/page.context.ts @@ -0,0 +1,5 @@ +export const pageContext = (title: string) => ({ + page: { + title + }, +}); \ No newline at end of file diff --git a/apps/app-htmx/src/context/tenant-list.context.ts b/apps/app-htmx/src/context/tenant-list.context.ts new file mode 100644 index 000000000..bbdafd10e --- /dev/null +++ b/apps/app-htmx/src/context/tenant-list.context.ts @@ -0,0 +1,17 @@ +import { getTenants } from "@acme/super-schema"; +import { createClient } from "@libsql/client"; +import { drizzle } from "drizzle-orm/libsql"; +import { AppConfig } from "src/app-config"; + +const loadTenants = async ()=> { + const superDb = drizzle(createClient(AppConfig.superDatabase)); + + const tenants = await getTenants(superDb); + + return tenants; +} +const TENANT_LIST = await loadTenants(); + +export const tenantListContext = () => ({ + tenantList: TENANT_LIST +}) \ No newline at end of file diff --git a/apps/app-htmx/src/functions/fetch-repository.ts b/apps/app-htmx/src/functions/fetch-repository.ts new file mode 100644 index 000000000..2e5387f77 --- /dev/null +++ b/apps/app-htmx/src/functions/fetch-repository.ts @@ -0,0 +1,42 @@ +import { GitHubSourceControl, GitlabSourceControl, type SourceControl } from "@acme/source-control"; +import { clerkClient } from "@clerk/fastify"; + +const getUserForgeAccessToken = async (userId: string, forge: "github" | "gitlab") => { + const userTokens = await clerkClient.users.getUserOauthAccessToken(userId, `oauth_${forge}`); + if (userTokens[0] === undefined) throw new Error("no token"); + return userTokens[0].token; +} + +type Props = { + forge: "github" | "gitlab"; + userId: string; + repositoryId: number; + repositoryName: string; + namespaceName: string; +} +export const tryFetchRepository = async ({ + userId, + forge, + namespaceName, + repositoryId, + repositoryName, +}: Props) => { + const token = await getUserForgeAccessToken(userId, forge); + + let sc: SourceControl; + if (forge === 'github') sc = new GitHubSourceControl(token); + else sc = new GitlabSourceControl(token); + + try { + const { repository, namespace } = await sc.fetchRepository(repositoryId, namespaceName, repositoryName); + return { repository, namespace }; + } catch (error) { + console.log("FAILED TO FETCH REPOSITORY"); + console.log(error); + } + + return { + repository: undefined, + namespace: undefined + } +} \ No newline at end of file diff --git a/apps/app-htmx/src/functions/get-crawl-stats.ts b/apps/app-htmx/src/functions/get-crawl-stats.ts new file mode 100644 index 000000000..a2c7f690b --- /dev/null +++ b/apps/app-htmx/src/functions/get-crawl-stats.ts @@ -0,0 +1,53 @@ +import type { Tenant } from "@acme/super-schema"; +import { type Client, createClient } from "@libsql/client"; +import { fromUnixTime } from "date-fns/fromUnixTime"; +import { formatDistanceToNow } from "date-fns/formatDistanceToNow"; +import { AppConfig } from "src/app-config"; +import { z } from "zod"; + +const formatDuration = (s: number) => { + const seconds = s % 60; + if (s < 60) return `${seconds}s` + const m = (s - seconds) / 60; + const minutes = m % 60; + if (m < 60) return `${minutes}m ${seconds.toString().padStart(2,'0')}s` + const h = (m - minutes) / 60; + return `${h}h ${minutes.toString().padStart(2, '0')}m ${seconds.toString().padStart(2, '0')}s` +} + +const rowSchema = z.object({ + crawl_instance: z.number(), + event_count: z.number(), + crawl_duration_sec: z.number(), + crawl_started_at: z.number() +}); + +const readCrawlStats =async (client:Client) => { + const x = await client.execute(` + SELECT + ce.instance_id as crawl_instance, + COUNT(ce.instance_id) as event_count, + MAX(ce.timestamp) - ci.started_at AS crawl_duration_sec, + ci.started_at as crawl_started_at +FROM crawl_events ce +JOIN crawl_instances ci ON ce.instance_id = ci.id +GROUP BY ce.instance_id; + `); + const rawStats = x.rows.map(row => rowSchema.parse(row)); + + return rawStats.map((rawStat)=>({ + instanceId: rawStat.crawl_instance, + eventCount: rawStat.event_count, + duration: formatDuration(rawStat.crawl_duration_sec), + startedAt: formatDistanceToNow(fromUnixTime(rawStat.crawl_started_at)) + })) +} + +export const getCrawlStats = async (tenant: Tenant) => { + const client = createClient({ + url: tenant.dbUrl, + authToken: AppConfig.tenantDatabaseAuthToken + }); + + return await readCrawlStats(client); +} \ No newline at end of file diff --git a/apps/app-htmx/src/functions/get-repositories.ts b/apps/app-htmx/src/functions/get-repositories.ts new file mode 100644 index 000000000..88279aaa1 --- /dev/null +++ b/apps/app-htmx/src/functions/get-repositories.ts @@ -0,0 +1,14 @@ +import { namespaces, repositories } from "@acme/extract-schema"; +import { eq } from "drizzle-orm"; +import type { LibSQLDatabase } from "drizzle-orm/libsql"; + +export const getRepositories = async (db: LibSQLDatabase) => { + const repos = await db.select({ + forge: repositories.forgeType, + name: repositories.name, + org: namespaces.name, + projectId: repositories.externalId, + }).from(repositories).innerJoin(namespaces, eq(repositories.namespaceId, namespaces.id)).all() + + return repos; +} \ No newline at end of file diff --git a/apps/app-htmx/src/index.ts b/apps/app-htmx/src/index.ts new file mode 100644 index 000000000..d5f55ac7b --- /dev/null +++ b/apps/app-htmx/src/index.ts @@ -0,0 +1,38 @@ +import Fastify from "fastify"; +import nunjucks from "nunjucks"; +import { fastifyView } from "@fastify/view"; +import path from "path"; +import { fileURLToPath } from "url"; +import { clerkPlugin } from "@clerk/fastify"; +import { Home } from "./pages/page.home.js"; +import fastifyFormbody from "@fastify/formbody"; +import { SignIn } from "./pages/page.sign-in.js"; +import { ExtractRepository } from "./pages/repository/extract.js"; +import { RegisterRepository } from "./pages/repository/register.js"; +import { AppConfig } from "./app-config.js"; +import { StartTransform } from "./pages/start-transform.js"; + +AppConfig; // ensure loaded before starting server + +const TEMPLATE_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), "../", "src", "views"); + +const fastify = Fastify({ logger: true }); + + +await fastify.register(fastifyView, { + engine: { + nunjucks: nunjucks, + }, + templates: TEMPLATE_DIR +}); +await fastify.register(clerkPlugin); +await fastify.register(fastifyFormbody); + +fastify.get('/', Home); +fastify.get('/sign-in',SignIn); +fastify.post('/repository/extract', ExtractRepository); +fastify.post('/repository/register', RegisterRepository); +fastify.post('/transform', StartTransform); + +await fastify.listen({ port: 3001, host: "127.0.0.1" }) +console.log("Server started on http://127.0.0.1:3001/"); \ No newline at end of file diff --git a/apps/app-htmx/src/pages/page.home.ts b/apps/app-htmx/src/pages/page.home.ts new file mode 100644 index 000000000..a57015bc6 --- /dev/null +++ b/apps/app-htmx/src/pages/page.home.ts @@ -0,0 +1,59 @@ +import { getAuth } from "@clerk/fastify"; +import type { RouteHandlerMethod } from "fastify"; +import { redirectToSignIn } from "./redirect-to-sign-in"; +import { pageContext } from "src/context/page.context"; +import { htmxContext } from "src/context/htmx.context"; +import { tenantListContext } from "src/context/tenant-list.context"; +import { drizzle } from "drizzle-orm/libsql"; +import { createClient } from "@libsql/client"; +import { getRepositories } from "src/functions/get-repositories"; +import { z } from "zod"; +import { AppConfig } from "src/app-config"; +import { getCrawlStats } from "src/functions/get-crawl-stats"; + +const QuerySchema = z.object({ + tenant: z.string().optional() +}); + +export const Home: RouteHandlerMethod = async (request, reply) => { + const auth = getAuth(request); + if (!auth.sessionId) return redirectToSignIn(reply); + + const parsedQuery = QuerySchema.safeParse(request.query); + const query = parsedQuery.success ? parsedQuery.data : {}; + + const page = pageContext("Home"); + const htmx = htmxContext(request.headers); + const tenantList = tenantListContext(); + + + const tenantQuery = query.tenant || tenantList.tenantList[0]!.name; + const tenant = tenantList.tenantList.find(t => t.name === tenantQuery); + if (!tenant) return reply.redirect(303, "/"); + const targetTenantId = tenant.id; + + const db = drizzle(createClient({ + url: tenant.dbUrl, + authToken: AppConfig.tenantDatabaseAuthToken + })); + + const repos = await getRepositories(db); + + const tenantCrawlStats = await getCrawlStats(tenant); + + return reply.view("page.home.html", { + auth, + ...page, + ...htmx, + ...tenantList, + targetTenant: tenant, + targetTenantId, + repos, + dates: { + yesterday: new Date(new Date().setDate(new Date().getDate() - 1)).toISOString().slice(0, 10), + today: new Date().toISOString().slice(0, 10) + }, + AppConfig, + tenantCrawlStats + }); +} diff --git a/apps/app-htmx/src/pages/page.sign-in.ts b/apps/app-htmx/src/pages/page.sign-in.ts new file mode 100644 index 000000000..bd979c3c6 --- /dev/null +++ b/apps/app-htmx/src/pages/page.sign-in.ts @@ -0,0 +1,17 @@ +import { getAuth } from "@clerk/fastify"; +import type { RouteHandlerMethod } from "fastify"; +import { pageContext } from "../context/page.context.js"; +import { htmxContext } from "../context/htmx.context.js"; +import { AppConfig } from "src/app-config.js"; + +export const SignIn: RouteHandlerMethod = async (request, reply) => { + const _auth = getAuth(request); + + const page = pageContext("Sign In"); + const htmx = htmxContext(request.headers); + return reply.view("page.sign-in.html", { + ...page, + ...htmx, + AppConfig + }); +} diff --git a/apps/app-htmx/src/pages/redirect-to-sign-in.ts b/apps/app-htmx/src/pages/redirect-to-sign-in.ts new file mode 100644 index 000000000..e76ab4940 --- /dev/null +++ b/apps/app-htmx/src/pages/redirect-to-sign-in.ts @@ -0,0 +1,3 @@ +import type { FastifyReply } from "fastify"; + +export const redirectToSignIn = (reply: FastifyReply) => reply.redirect(303, '/sign-in'); \ No newline at end of file diff --git a/apps/app-htmx/src/pages/repository/extract.ts b/apps/app-htmx/src/pages/repository/extract.ts new file mode 100644 index 000000000..e69d32615 --- /dev/null +++ b/apps/app-htmx/src/pages/repository/extract.ts @@ -0,0 +1,72 @@ +import type { RouteHandlerMethod } from "fastify"; +import { clerkClient, getAuth } from "@clerk/fastify" +import { z } from "zod"; +import { tenantListContext } from "src/context/tenant-list.context"; +import { AppConfig } from "src/app-config"; + +const ExtractInputSchema = z.object({ + target_tenant_id: z.coerce.number(), + forge: z.literal("github").or(z.literal("gitlab")), + owner: z.string().optional(), + repo: z.string().optional(), + project_id: z.coerce.number().optional() +}).and(z.discriminatedUnion("qtype", [ + z.object({ + qtype: z.literal('last'), + last: z.coerce.number(), + }), + z.object({ + qtype: z.literal('between'), + from: z.coerce.date(), + to: z.coerce.date(), + }) +])); + +export const ExtractRepository: RouteHandlerMethod = async (request, reply) => { + const auth = getAuth(request); + const {tenantList} = tenantListContext(); + if (!auth.userId) return reply.status(404).send(); + const parsedExtractInput = ExtractInputSchema.safeParse(request.body); + + if (!parsedExtractInput.success) return reply.view("component.log.html", { error: parsedExtractInput.error }); + + const extractInput = parsedExtractInput.data; + const tenant = tenantList.find(tenant=>tenant.id === extractInput.target_tenant_id); + if (!tenant) return reply.view("component.log.html", { error: `Invalid target_tenant_id: ${extractInput.target_tenant_id}` }); + + let to = new Date(); + let from = new Date(to); + if (extractInput.qtype === 'last') from.setDate(to.getDate() - extractInput.last); + if (extractInput.qtype === 'between') { + from = extractInput.from; + to = extractInput.to; + // from.setUTCHours(0, 0, 0, 0); + // to.setUTCHours(23, 59, 59, 999); + } + + const requestBody = JSON.stringify({ + repositoryId: extractInput.project_id || 0, + repositoryName: extractInput.repo || "", + namespaceName: extractInput.owner || "", + sourceControl: extractInput.forge, + from, + to, + tenantId: tenant.id, + }); + + const apiToken = await clerkClient.sessions.getToken(auth.sessionId, "dashboard") as string; + + const res = await fetch(AppConfig.apis.extractStart, { + method: 'post', + body: requestBody, + headers: { + 'Authorization': 'Bearer ' + apiToken + } + }); + + const body = await res.text(); + + if (res.status !== 200) return reply.view('component.log.html', { error: body }); + + return reply.view('component.log.html', { log: body }); +} \ No newline at end of file diff --git a/apps/app-htmx/src/pages/repository/register.ts b/apps/app-htmx/src/pages/repository/register.ts new file mode 100644 index 000000000..8d6e4bf52 --- /dev/null +++ b/apps/app-htmx/src/pages/repository/register.ts @@ -0,0 +1,57 @@ +import type { RouteHandlerMethod } from "fastify"; +import { getAuth } from "@clerk/fastify" +import { z } from "zod"; +import { tryFetchRepository } from "src/functions/fetch-repository"; +import { tenantListContext } from "src/context/tenant-list.context"; + +const RegisterInput = z.object({ + target_tenant_id: z.coerce.number(), + forge: z.literal("github").or(z.literal("gitlab")), + owner: z.string().optional(), + repo: z.string().optional(), + project_id: z.string().optional() +}) + +export const RegisterRepository: RouteHandlerMethod = async (request, reply) => { + const auth = getAuth(request); + if (!auth.userId) return reply.status(404).send(); // hide actions from unauthenticated users + + const { tenantList } = tenantListContext(); + + const safeInput = RegisterInput.safeParse(request.body); + if (!safeInput.success) return reply.status(400).send(); + + const input = { + userId: auth.userId, + forge: safeInput.data.forge, + namespaceName: safeInput.data.owner || "", + repositoryName: safeInput.data.repo || "", + repositoryId: Number(safeInput.data.project_id) || 0 + } + + const { repository, namespace } = await tryFetchRepository(input); + + if (!repository || !namespace) return reply.view("component.log.html", + safeInput.data.forge === "github" ? { error: `Repository ${input.namespaceName}/${input.repositoryName} not found` } + : { error: `Project ${input.repositoryId} not found` }); + + const targetTenantId = safeInput.data.target_tenant_id; + const tenant = tenantList.find(tenant=>tenant.id === targetTenantId); + if (!tenant) return reply.view("component.log.html", { error: `Invalid target_tenant_id: ${targetTenantId}` }); + + const repo = { + name: repository.name, + org: namespace.name, + forge: repository.forgeType, + projectId: repository.externalId + } + + return reply.view("component.repository.html", { + repo, + targetTenantId, + dates: { + yesterday: new Date(new Date().setDate(new Date().getDate() - 1)).toISOString().slice(0, 10), + today: new Date().toISOString().slice(0, 10) + } + }); +} \ No newline at end of file diff --git a/apps/app-htmx/src/pages/start-transform.ts b/apps/app-htmx/src/pages/start-transform.ts new file mode 100644 index 000000000..076d1c303 --- /dev/null +++ b/apps/app-htmx/src/pages/start-transform.ts @@ -0,0 +1,39 @@ +import type { RouteHandlerMethod } from "fastify"; +import { clerkClient, getAuth } from "@clerk/fastify" +import { z } from "zod"; +import { tenantListContext } from "src/context/tenant-list.context"; +import { AppConfig } from "src/app-config"; + +const TransformInputSchema = z.object({ + target_tenant_id: z.coerce.number(), +}); + +export const StartTransform: RouteHandlerMethod = async (request, reply) => { + const auth = getAuth(request); + const {tenantList} = tenantListContext(); + if (!auth.userId) return reply.status(404).send(); + const parsedTransformInput = TransformInputSchema.safeParse(request.body); + + if (!parsedTransformInput.success) return reply.view("component.log.html", { error: parsedTransformInput.error }); + + const extractInput = parsedTransformInput.data; + const tenant = tenantList.find(tenant=>tenant.id === extractInput.target_tenant_id); + if (!tenant) return reply.view("component.log.html", { error: `Invalid target_tenant_id: ${extractInput.target_tenant_id}` }); + + const apiToken = await clerkClient.sessions.getToken(auth.sessionId, "dashboard") as string; + const requestBody = JSON.stringify({ tenantId: tenant.id }); + + const res = await fetch(AppConfig.apis.transformStart, { + method: 'post', + body: requestBody, + headers: { + 'Authorization': 'Bearer ' + apiToken + } + }); + + const body = await res.text(); + + if (res.status !== 200) return reply.view('component.log.html', { error: body }); + + return reply.view('component.log.html', { log: body }); +} \ No newline at end of file diff --git a/apps/app-htmx/src/plugins/auth.plugin.ts b/apps/app-htmx/src/plugins/auth.plugin.ts new file mode 100644 index 000000000..f088fc12a --- /dev/null +++ b/apps/app-htmx/src/plugins/auth.plugin.ts @@ -0,0 +1,13 @@ +import { getAuth } from "@clerk/fastify"; +import type { FastifyPluginAsync } from "fastify"; + +export const authPlugin: FastifyPluginAsync = async (fastify) => { + fastify.addHook('preHandler', (request, reply) => { + const auth = getAuth(request); + if (!auth.userId) { + return reply.redirect(303, '/sign-in'); + } + return Promise.resolve(); + }); + return Promise.resolve(); +} \ No newline at end of file diff --git a/apps/app-htmx/src/views/component.crawl-stats.html b/apps/app-htmx/src/views/component.crawl-stats.html new file mode 100644 index 000000000..51c320f61 --- /dev/null +++ b/apps/app-htmx/src/views/component.crawl-stats.html @@ -0,0 +1,22 @@ +
+ + + + + + + + + + + {% for stat in tenantCrawlStats %} + + + + + + + {% endfor %} + +
WhenIdEvent CountDuration
{{stat.startedAt}} ago{{stat.instanceId}}{{stat.eventCount}}{{stat.duration}}
+
\ No newline at end of file diff --git a/apps/app-htmx/src/views/component.log.html b/apps/app-htmx/src/views/component.log.html new file mode 100644 index 000000000..b551b0744 --- /dev/null +++ b/apps/app-htmx/src/views/component.log.html @@ -0,0 +1,5 @@ +{% if log %} +
{{log}}
+{% elseif error %} +
{{error}}
+{% endif %} \ No newline at end of file diff --git a/apps/app-htmx/src/views/component.repository.html b/apps/app-htmx/src/views/component.repository.html new file mode 100644 index 000000000..955cc3a3c --- /dev/null +++ b/apps/app-htmx/src/views/component.repository.html @@ -0,0 +1,40 @@ +
+
+ {% if repo.forge == "gitlab" %} + + {% else %} + + {% endif %} +

{{repo.org}}/{{repo.name}}

+
+
+ + + + + + + + +
+
+ +
+ + +
+
+ +
+
+
\ No newline at end of file diff --git a/apps/app-htmx/src/views/layout.app.boosted.html b/apps/app-htmx/src/views/layout.app.boosted.html new file mode 100644 index 000000000..478690e36 --- /dev/null +++ b/apps/app-htmx/src/views/layout.app.boosted.html @@ -0,0 +1,3 @@ +{{ page.title }} +{% block content %} +{% endblock %} \ No newline at end of file diff --git a/apps/app-htmx/src/views/layout.app.default.html b/apps/app-htmx/src/views/layout.app.default.html new file mode 100644 index 000000000..a731b38b2 --- /dev/null +++ b/apps/app-htmx/src/views/layout.app.default.html @@ -0,0 +1,56 @@ +{% extends "root.html" %} + +{% block body %} + + +
+ {% block content %} + {% endblock %} +
+ +{% endblock %} diff --git a/apps/app-htmx/src/views/layout.app.html b/apps/app-htmx/src/views/layout.app.html new file mode 100644 index 000000000..7a68dc7cb --- /dev/null +++ b/apps/app-htmx/src/views/layout.app.html @@ -0,0 +1,5 @@ +{% if not htmx.boosted %} + {% extends "layout.app.default.html" %} +{% else %} + {% extends "layout.app.boosted.html" %} +{% endif %} \ No newline at end of file diff --git a/apps/app-htmx/src/views/page.home.html b/apps/app-htmx/src/views/page.home.html new file mode 100644 index 000000000..bc3a9146b --- /dev/null +++ b/apps/app-htmx/src/views/page.home.html @@ -0,0 +1,37 @@ +{% extends "layout.app.html" %} + +{% block content %} +

{{targetTenant.name}}

+

Register Repository

+
+ + + / + + +
+

Repositories

+
+ {% for repo in repos %} + {% include "component.repository.html" %} + {% endfor %} +
+
+
+ + +
+
+

Logs

+
+
+

Crawl Stats

+
+ {% include "component.crawl-stats.html" %} +
+ +{% endblock %} \ No newline at end of file diff --git a/apps/app-htmx/src/views/page.sign-in.html b/apps/app-htmx/src/views/page.sign-in.html new file mode 100644 index 000000000..95c976de4 --- /dev/null +++ b/apps/app-htmx/src/views/page.sign-in.html @@ -0,0 +1,24 @@ +{% extends "root.html" %} + +{% block body %} +
+ +{% endblock %} \ No newline at end of file diff --git a/apps/app-htmx/src/views/root.html b/apps/app-htmx/src/views/root.html new file mode 100644 index 000000000..fd88611bb --- /dev/null +++ b/apps/app-htmx/src/views/root.html @@ -0,0 +1,56 @@ + + + + + + + + {{ page.title }} + + + + {% block body %} + {% endblock %} + + \ No newline at end of file diff --git a/apps/app-htmx/tsconfig.json b/apps/app-htmx/tsconfig.json new file mode 100644 index 000000000..19ce97069 --- /dev/null +++ b/apps/app-htmx/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + }, + "exclude": [], + "include": [ + "src/**/*.ts", + "*.mjs", + ] +} diff --git a/package-lock.json b/package-lock.json index 170785395..2f34456cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,6 @@ "version": "0.1.0", "dependencies": { "@acme/eslint-config": "*", - "@acme/super-schema": "*", "@acme/tailwind-config": "*", "@clerk/nextjs": "^4.23.2", "@radix-ui/react-avatar": "^1.0.3", @@ -64,6 +63,24 @@ "postcss": "^8.4.32" } }, + "apps/app-htmx": { + "version": "0.1.0", + "dependencies": { + "@acme/source-control": "*", + "@acme/super-schema": "*", + "@clerk/fastify": "0.6.30", + "@fastify/formbody": "7.4.0", + "@fastify/view": "8.2.0", + "date-fns": "3.3.1", + "fastify": "4.25.2", + "nunjucks": "3.2.4" + }, + "devDependencies": { + "@types/nunjucks": "3.2.6", + "dotenv-cli": "^7.3.0", + "esbuild": "^0.19.9" + } + }, "apps/stack": { "name": "@acme/stack", "version": "1.0.0", @@ -119,6 +136,10 @@ "resolved": "apps/app", "link": true }, + "node_modules/@acme/app-htmx": { + "resolved": "apps/app-htmx", + "link": true + }, "node_modules/@acme/crawl-functions": { "resolved": "packages/functions/crawl", "link": true @@ -3058,6 +3079,82 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@clerk/fastify": { + "version": "0.6.30", + "resolved": "https://registry.npmjs.org/@clerk/fastify/-/fastify-0.6.30.tgz", + "integrity": "sha512-Opgtc9XZXcgumSjsAsbPvsLGVNjVda403FeP2N4SXlcGRP0+dEpa9g0hTIAWth51aD1D1Jp8shTs74lPh2NfoQ==", + "dependencies": { + "@clerk/backend": "0.37.1", + "@clerk/shared": "1.3.1", + "@clerk/types": "3.60.0", + "cookies": "0.8.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "fastify": ">=4", + "fastify-plugin": "^4.5.0" + } + }, + "node_modules/@clerk/fastify/node_modules/@clerk/backend": { + "version": "0.37.1", + "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-0.37.1.tgz", + "integrity": "sha512-+KNdl2QtAHGRe5DQSGNJ8FCPFWhG8+HnVw5gHtojCxvTgxvOVUaxDqzytYl2qDd2zjzBDiNqwTd7J+5sEXRdkg==", + "dependencies": { + "@clerk/shared": "1.3.1", + "@clerk/types": "3.60.0", + "@peculiar/webcrypto": "1.4.1", + "@types/node": "16.18.6", + "cookie": "0.5.0", + "deepmerge": "4.2.2", + "node-fetch-native": "1.0.1", + "snakecase-keys": "5.4.4", + "tslib": "2.4.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@clerk/fastify/node_modules/@clerk/shared": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-1.3.1.tgz", + "integrity": "sha512-nzv4+uA90I/eQp55zfK9a1Po9VgCYlzlNhuZnKqyRsPyJ38l4gpIf3B3qSHHdN0+MTx9cWGFrik1CnpftdOBXQ==", + "dependencies": { + "glob-to-regexp": "0.4.1", + "js-cookie": "3.0.1", + "swr": "2.2.0" + }, + "peerDependencies": { + "react": ">=16" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/@clerk/fastify/node_modules/@types/node": { + "version": "16.18.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.6.tgz", + "integrity": "sha512-vmYJF0REqDyyU0gviezF/KHq/fYaUbFhkcNbQCuPGFQj6VTbXuHZoxs/Y7mutWe73C8AC6l9fFu8mSYiBAqkGA==" + }, + "node_modules/@clerk/fastify/node_modules/swr": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.0.tgz", + "integrity": "sha512-AjqHOv2lAhkuUdIiBu9xbuettzAzWXmCEcLONNKJRba87WAefz8Ca9d6ds/SzrPc235n1IxWYdhJ2zF3MNUaoQ==", + "dependencies": { + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@clerk/fastify/node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, "node_modules/@clerk/nextjs": { "version": "4.23.2", "license": "MIT", @@ -3095,8 +3192,9 @@ } }, "node_modules/@clerk/types": { - "version": "3.51.0", - "license": "MIT", + "version": "3.60.0", + "resolved": "https://registry.npmjs.org/@clerk/types/-/types-3.60.0.tgz", + "integrity": "sha512-f1A16wFh5MtikxEo7o6vAVX7FxpqC1YmzA6c4ugwq5MH8J2mvIM/LwNVIHgNpZkn/s/G+BUhBcJJmUXqajDK2Q==", "dependencies": { "csstype": "3.1.1" }, @@ -4134,6 +4232,72 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fastify/ajv-compiler": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.5.0.tgz", + "integrity": "sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==", + "dependencies": { + "ajv": "^8.11.0", + "ajv-formats": "^2.1.1", + "fast-uri": "^2.0.0" + } + }, + "node_modules/@fastify/ajv-compiler/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "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/@fastify/ajv-compiler/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/@fastify/deepmerge": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.3.0.tgz", + "integrity": "sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==" + }, + "node_modules/@fastify/error": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", + "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==" + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", + "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", + "dependencies": { + "fast-json-stringify": "^5.7.0" + } + }, + "node_modules/@fastify/formbody": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@fastify/formbody/-/formbody-7.4.0.tgz", + "integrity": "sha512-H3C6h1GN56/SMrZS8N2vCT2cZr7mIHzBHzOBa5OPpjfB/D6FzP9mMpE02ZzrFX0ANeh0BAJdoXKOF2e7IbV+Og==", + "dependencies": { + "fast-querystring": "^1.0.0", + "fastify-plugin": "^4.0.0" + } + }, + "node_modules/@fastify/view": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@fastify/view/-/view-8.2.0.tgz", + "integrity": "sha512-hBSiBofCnJNlPHEMZWpO1SL84eqOaqujJ1hR3jntFyZZCkweH5jMs12DKYyGesjVll7SJFRRxPUBB8kmUmneRQ==", + "dependencies": { + "fastify-plugin": "^4.0.0", + "hashlru": "^2.3.0" + } + }, "node_modules/@floating-ui/core": { "version": "1.3.1", "license": "MIT" @@ -7259,6 +7423,12 @@ "@types/node": "*" } }, + "node_modules/@types/nunjucks": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@types/nunjucks/-/nunjucks-3.2.6.tgz", + "integrity": "sha512-pHiGtf83na1nCzliuAdq8GowYiXvH5l931xZ0YEHaLMNFgynpEqx+IPStlu7UaDkehfvl01e4x/9Tpwhy7Ue3w==", + "dev": true + }, "node_modules/@types/parse-link-header": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/parse-link-header/-/parse-link-header-2.0.3.tgz", @@ -7597,6 +7767,27 @@ "tslib": "^2.3.1" } }, + "node_modules/a-sync-waterfall": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", + "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" + }, + "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/accepts": { "version": "1.3.8", "license": "MIT", @@ -7809,6 +8000,11 @@ "safe-buffer": "~5.1.0" } }, + "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/are-we-there-yet": { "version": "1.0.6", "license": "ISC", @@ -7957,6 +8153,11 @@ "printable-characters": "^1.0.42" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, "node_modules/asn1.js": { "version": "5.4.1", "license": "MIT", @@ -8014,6 +8215,14 @@ "node": ">= 4.0.0" } }, + "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/atomically": { "version": "1.7.0", "license": "MIT", @@ -8073,6 +8282,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/avvio": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.1.tgz", + "integrity": "sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==", + "dependencies": { + "archy": "^1.0.0", + "debug": "^4.0.0", + "fastq": "^1.6.1" + } + }, "node_modules/aws-cdk-lib": { "version": "2.84.0", "bundleDependencies": [ @@ -10880,6 +11099,18 @@ "version": "1.0.6", "license": "MIT" }, + "node_modules/cookies": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", + "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", + "dependencies": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/copy-anything": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", @@ -10985,6 +11216,15 @@ "node": ">= 12" } }, + "node_modules/date-fns": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.3.1.tgz", + "integrity": "sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debounce-fn": { "version": "4.0.0", "license": "MIT", @@ -12484,6 +12724,14 @@ "es5-ext": "~0.10.14" } }, + "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/events": { "version": "1.1.1", "license": "MIT", @@ -12638,6 +12886,11 @@ "dev": true, "license": "ISC" }, + "node_modules/fast-content-type-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", + "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==" + }, "node_modules/fast-decode-uri-component": { "version": "1.0.1", "license": "MIT" @@ -12674,6 +12927,40 @@ "version": "2.1.0", "license": "MIT" }, + "node_modules/fast-json-stringify": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.11.1.tgz", + "integrity": "sha512-Lrj3tmc/qI1OCmr0WJEJFC4TxnAdLnbZZ0CvFvHv/PHNieLCX12PNBlV9KGIDS08w49T8h7vkMuzNoB+3NyX0g==", + "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", + "json-schema-ref-resolver": "^1.0.1", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-json-stringify/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "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/fast-json-stringify/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/fast-jwt": { "version": "1.7.2", "license": "Apache-2.0", @@ -12697,6 +12984,19 @@ "fast-decode-uri-component": "^1.0.1" } }, + "node_modules/fast-redact": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz", + "integrity": "sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-uri": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.3.0.tgz", + "integrity": "sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==" + }, "node_modules/fast-url-parser": { "version": "1.1.3", "license": "MIT", @@ -12728,6 +13028,34 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/fastify": { + "version": "4.25.2", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.25.2.tgz", + "integrity": "sha512-SywRouGleDHvRh054onj+lEZnbC1sBCLkR0UY3oyJwjD4BdZJUrxBqfkfCaqn74pVCwBaRHGuL3nEWeHbHzAfw==", + "dependencies": { + "@fastify/ajv-compiler": "^3.5.0", + "@fastify/error": "^3.4.0", + "@fastify/fast-json-stringify-compiler": "^4.3.0", + "abstract-logging": "^2.0.1", + "avvio": "^8.2.1", + "fast-content-type-parse": "^1.1.0", + "fast-json-stringify": "^5.8.0", + "find-my-way": "^7.7.0", + "light-my-request": "^5.11.0", + "pino": "^8.17.0", + "process-warning": "^3.0.0", + "proxy-addr": "^2.0.7", + "rfdc": "^1.3.0", + "secure-json-parse": "^2.7.0", + "semver": "^7.5.4", + "toad-cache": "^3.3.0" + } + }, + "node_modules/fastify-plugin": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.1.tgz", + "integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==" + }, "node_modules/fastq": { "version": "1.15.0", "license": "ISC", @@ -12818,6 +13146,19 @@ "version": "2.0.0", "license": "MIT" }, + "node_modules/find-my-way": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.7.0.tgz", + "integrity": "sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/find-up": { "version": "4.1.0", "license": "MIT", @@ -13401,6 +13742,11 @@ "version": "6.0.0", "license": "MIT" }, + "node_modules/hashlru": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/hashlru/-/hashlru-2.3.0.tgz", + "integrity": "sha512-0cMsjjIC8I+D3M44pOQdsy0OHXGLVz6Z0beRuufhKa0KfaD2wGwAev6jILzXsd3/vpnNQJmWyZtIILqM1N+n5A==" + }, "node_modules/heap": { "version": "0.2.7", "dev": true, @@ -16055,6 +16401,14 @@ "license": "MIT", "peer": true }, + "node_modules/json-schema-ref-resolver": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", + "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "license": "MIT" @@ -16095,6 +16449,17 @@ "node": ">=4.0" } }, + "node_modules/keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "dependencies": { + "tsscmp": "1.0.6" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -16320,6 +16685,21 @@ "@libsql/win32-x64-msvc": "0.1.34" } }, + "node_modules/light-my-request": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.11.0.tgz", + "integrity": "sha512-qkFCeloXCOMpmEdZ/MV91P8AT4fjwFXWaAFz3lUeStM8RcoM1ks4J/F8r1b3r6y/H4u3ACEJ1T+Gv5bopj7oDA==", + "dependencies": { + "cookie": "^0.5.0", + "process-warning": "^2.0.0", + "set-cookie-parser": "^2.4.1" + } + }, + "node_modules/light-my-request/node_modules/process-warning": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz", + "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==" + }, "node_modules/lilconfig": { "version": "2.1.0", "license": "MIT", @@ -17148,6 +17528,38 @@ "node": ">=0.10.0" } }, + "node_modules/nunjucks": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", + "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", + "dependencies": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + }, + "bin": { + "nunjucks-precompile": "bin/precompile" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "chokidar": "^3.3.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/nunjucks/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-assign": { "version": "4.1.1", "license": "MIT", @@ -17256,6 +17668,14 @@ "node": "^10.13.0 || >=12.0.0" } }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/on-finished": { "version": "2.4.1", "license": "MIT", @@ -17586,6 +18006,95 @@ "node": ">=6" } }, + "node_modules/pino": { + "version": "8.17.2", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.17.2.tgz", + "integrity": "sha512-LA6qKgeDMLr2ux2y/YiUt47EfgQ+S9LznBWOJdN3q1dx2sv0ziDLUBeVpyVv17TEcGCBuWf0zNtg3M5m1NhhWQ==", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "v1.1.0", + "pino-std-serializers": "^6.0.0", + "process-warning": "^3.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.7.0", + "thread-stream": "^2.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz", + "integrity": "sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==", + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/pino-abstract-transport/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/pino-abstract-transport/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/pino-abstract-transport/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/pino-abstract-transport/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/pino-std-serializers": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", + "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==" + }, "node_modules/pirates": { "version": "4.0.5", "license": "MIT", @@ -17936,6 +18445,11 @@ "version": "2.0.1", "license": "MIT" }, + "node_modules/process-warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" + }, "node_modules/promptly": { "version": "3.2.0", "license": "MIT", @@ -18067,6 +18581,11 @@ ], "license": "MIT" }, + "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/quick-lru": { "version": "4.0.1", "license": "MIT", @@ -18318,6 +18837,14 @@ "node": ">=8.10.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/regenerator-runtime": { "version": "0.13.11", "license": "MIT" @@ -18495,6 +19022,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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", "license": "MIT", @@ -18689,6 +19224,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "license": "MIT" @@ -18704,6 +19255,11 @@ "loose-envify": "^1.1.0" } }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" + }, "node_modules/selfsigned": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", @@ -18809,6 +19365,11 @@ "node": ">= 0.8.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", + "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" + }, "node_modules/setimmediate": { "version": "1.0.5", "license": "MIT" @@ -18971,6 +19532,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/sonic-boom": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.0.tgz", + "integrity": "sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "dev": true, @@ -20234,6 +20803,14 @@ "node": ">=0.8" } }, + "node_modules/thread-stream": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz", + "integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==", + "dependencies": { + "real-require": "^0.2.0" + } + }, "node_modules/timers-ext": { "version": "0.1.7", "dev": true, @@ -20293,6 +20870,14 @@ "to-no-case": "^1.0.0" } }, + "node_modules/toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "engines": { + "node": ">=12" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "license": "MIT", @@ -20437,6 +21022,14 @@ "version": "2.5.3", "license": "0BSD" }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "engines": { + "node": ">=0.6.x" + } + }, "node_modules/tsutils": { "version": "3.21.0", "license": "MIT", @@ -20812,6 +21405,14 @@ } } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util": { "version": "0.12.5", "license": "MIT", From 61bb3e656621f4bdba292c8a22531a1f256c865a Mon Sep 17 00:00:00 2001 From: dejan-crocoder Date: Thu, 1 Feb 2024 12:56:59 +0100 Subject: [PATCH 2/3] chore: respond with from-to in extract api --- apps/stack/src/extract/extract-repository.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/stack/src/extract/extract-repository.ts b/apps/stack/src/extract/extract-repository.ts index 7b1ba7478..7f1149560 100644 --- a/apps/stack/src/extract/extract-repository.ts +++ b/apps/stack/src/extract/extract-repository.ts @@ -117,7 +117,7 @@ export const handler = ApiHandler(async (ev) => { return { statusCode: 200, - body: JSON.stringify({}) + body: JSON.stringify({ from: inputValidation.data.from, to: inputValidation.data.to }) }; }); From 43d7e039f18beb50482368feb662e4cef34e25eb Mon Sep 17 00:00:00 2001 From: dejan-crocoder Date: Thu, 1 Feb 2024 13:13:35 +0100 Subject: [PATCH 3/3] add environment variable APP_HTMX_PORT --- apps/app-htmx/src/app-config.ts | 2 ++ apps/app-htmx/src/index.ts | 6 ++++-- turbo.json | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/app-htmx/src/app-config.ts b/apps/app-htmx/src/app-config.ts index 9ec9cbef8..db538e5aa 100644 --- a/apps/app-htmx/src/app-config.ts +++ b/apps/app-htmx/src/app-config.ts @@ -12,12 +12,14 @@ const ENVSchema = z.object({ CLERK_PUBLISHABLE_KEY: z.string(), CLERK_SECRET_KEY: z.string(), CLERK_DOMAIN: z.string(), + APP_HTMX_PORT: z.coerce.number().optional(), }); const parsedEnv = ENVSchema.safeParse(process.env); if (!parsedEnv.success) throw new Error(`Invalid environment: ${parsedEnv.error}`); export const AppConfig = { + port: parsedEnv.data.APP_HTMX_PORT, superDatabase: { url: parsedEnv.data.SUPER_DATABASE_URL, authToken: parsedEnv.data.SUPER_DATABASE_AUTH_TOKEN, diff --git a/apps/app-htmx/src/index.ts b/apps/app-htmx/src/index.ts index d5f55ac7b..77f72bce7 100644 --- a/apps/app-htmx/src/index.ts +++ b/apps/app-htmx/src/index.ts @@ -34,5 +34,7 @@ fastify.post('/repository/extract', ExtractRepository); fastify.post('/repository/register', RegisterRepository); fastify.post('/transform', StartTransform); -await fastify.listen({ port: 3001, host: "127.0.0.1" }) -console.log("Server started on http://127.0.0.1:3001/"); \ No newline at end of file +const PORT = AppConfig.port || 3001; + +await fastify.listen({ port: PORT, host: "127.0.0.1" }) +console.log(`Server started on http://127.0.0.1:${PORT}/`); \ No newline at end of file diff --git a/turbo.json b/turbo.json index 0984fdfd4..cd95c22b1 100644 --- a/turbo.json +++ b/turbo.json @@ -34,6 +34,7 @@ "NEXT_PUBLIC_CLERK_SIGN_UP_URL", "NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL", "NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL", + "APP_HTMX_PORT", "TENANT_DATABASE_URL", "TENANT_DATABASE_AUTH_TOKEN", "SUPER_DATABASE_URL",