diff --git a/drizzle-kit b/drizzle-kit new file mode 100644 index 0000000..e69de29 diff --git a/drizzle-pg.config.ts b/drizzle-pg.config.ts index 6ed0922..0ff4a35 100644 --- a/drizzle-pg.config.ts +++ b/drizzle-pg.config.ts @@ -10,6 +10,6 @@ export default defineConfig({ schema: "./src/db/pg/schema.ts", out: "./drizzle-pg", dbCredentials: { - url: process.env.POSTGRES_DATABASE_URL!, + url: "postgres://openseo_user:Metatron2026@localhost:5432/openseo", }, }); diff --git a/open-seo@0.1.7 b/open-seo@0.1.7 new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json index 485064a..0f79432 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "packageManager": "pnpm@10.30.1", "scripts": { "dev": "vite dev", + "start": " vite preview --port 3001", "dev:clear-chat": "rm -rf .wrangler/state/v3/do/open-seo-OnboardingChatAgent", "dev:agents": "mkdir -p .logs && portless run vite dev 2>&1 | tee .logs/dev-server.log", "dev:agents:force": "mkdir -p .logs && portless --force run vite dev 2>&1 | tee .logs/dev-server.log", diff --git a/vite.config.ts b/vite.config.ts index 5bb4cc1..f329d97 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,5 +1,5 @@ import { tanstackStart } from "@tanstack/react-start/plugin/vite"; -import { defineConfig, loadEnv } from "vite"; +import { defineConfig } from "vite"; import tsConfigPaths from "vite-tsconfig-paths"; import viteReact from "@vitejs/plugin-react"; import tailwindcss from "@tailwindcss/vite"; @@ -7,19 +7,14 @@ import { cloudflare } from "@cloudflare/vite-plugin"; import { devtools } from "@tanstack/devtools-vite"; import { leanWorkerBundle } from "./vite-plugin-lean-worker-bundle"; -export default defineConfig(({ mode }) => { - const env = loadEnv(mode, process.cwd(), ""); - const port = process.env.PORT - ? Number(process.env.PORT) - : env.PORT - ? Number(env.PORT) - : 3001; - const showDevtools = env.VITE_SHOW_DEVTOOLS !== "false"; +export default defineConfig(() => { + const port = 3001; + + const showDevtools = false; + const allowedHosts = [ - env.ALLOWED_HOST, - env.BETTER_AUTH_URL ? new URL(env.BETTER_AUTH_URL).hostname : undefined, - ].filter((host): host is string => Boolean(host)); - const emitSourcemaps = env.POSTHOG_SOURCEMAPS === "true"; + "seo.thedomainnest.com", + ]; return { envPrefix: [ @@ -30,20 +25,27 @@ export default defineConfig(({ mode }) => { "POSTHOG_HOST", "TURNSTILE_SITE_KEY", ], + server: { - allowedHosts, + host: "0.0.0.0", port, + allowedHosts, }, + preview: { - allowedHosts, + host: "0.0.0.0", port, + allowedHosts, }, + build: { - sourcemap: emitSourcemaps, - outDir: emitSourcemaps ? "dist-sourcemaps" : "dist", + sourcemap: false, + outDir: "dist", }, + plugins: [ leanWorkerBundle(), + showDevtools ? devtools({ consolePiping: { @@ -52,18 +54,23 @@ export default defineConfig(({ mode }) => { }, }) : null, + cloudflare({ inspectorPort: false, viteEnvironment: { name: "ssr" }, - // The site-audit aux worker builds to dist/open_seo_audit/ and runs - // beside the main worker in dev and preview, with the app's - // cross-script SITE_AUDIT_WORKFLOW / AUDIT_SCRATCHPAD bindings - // resolved against it. - auxiliaryWorkers: [{ configPath: "./wrangler.audit.jsonc" }], + auxiliaryWorkers: [ + { + configPath: "./wrangler.audit.jsonc", + }, + ], }), + tsConfigPaths(), + tanstackStart(), + viteReact(), + tailwindcss(), ], }; diff --git a/vite.config.ts.backup b/vite.config.ts.backup new file mode 100644 index 0000000..5bb4cc1 --- /dev/null +++ b/vite.config.ts.backup @@ -0,0 +1,70 @@ +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import { defineConfig, loadEnv } from "vite"; +import tsConfigPaths from "vite-tsconfig-paths"; +import viteReact from "@vitejs/plugin-react"; +import tailwindcss from "@tailwindcss/vite"; +import { cloudflare } from "@cloudflare/vite-plugin"; +import { devtools } from "@tanstack/devtools-vite"; +import { leanWorkerBundle } from "./vite-plugin-lean-worker-bundle"; + +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), ""); + const port = process.env.PORT + ? Number(process.env.PORT) + : env.PORT + ? Number(env.PORT) + : 3001; + const showDevtools = env.VITE_SHOW_DEVTOOLS !== "false"; + const allowedHosts = [ + env.ALLOWED_HOST, + env.BETTER_AUTH_URL ? new URL(env.BETTER_AUTH_URL).hostname : undefined, + ].filter((host): host is string => Boolean(host)); + const emitSourcemaps = env.POSTHOG_SOURCEMAPS === "true"; + + return { + envPrefix: [ + "VITE_", + "AUTH_MODE", + "BYPASS_EMAIL_VERIFICATION", + "POSTHOG_PUBLIC_KEY", + "POSTHOG_HOST", + "TURNSTILE_SITE_KEY", + ], + server: { + allowedHosts, + port, + }, + preview: { + allowedHosts, + port, + }, + build: { + sourcemap: emitSourcemaps, + outDir: emitSourcemaps ? "dist-sourcemaps" : "dist", + }, + plugins: [ + leanWorkerBundle(), + showDevtools + ? devtools({ + consolePiping: { + enabled: true, + levels: ["log", "warn", "error", "info", "debug"], + }, + }) + : null, + cloudflare({ + inspectorPort: false, + viteEnvironment: { name: "ssr" }, + // The site-audit aux worker builds to dist/open_seo_audit/ and runs + // beside the main worker in dev and preview, with the app's + // cross-script SITE_AUDIT_WORKFLOW / AUDIT_SCRATCHPAD bindings + // resolved against it. + auxiliaryWorkers: [{ configPath: "./wrangler.audit.jsonc" }], + }), + tsConfigPaths(), + tanstackStart(), + viteReact(), + tailwindcss(), + ], + }; +}); diff --git a/wrangler.jsonc b/wrangler.jsonc index 50a5349..2edf586 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -3,146 +3,122 @@ "name": "open-seo", "main": "src/server.ts", "compatibility_date": "2025-09-02", - "compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"], + "compatibility_flags": [ + "nodejs_compat", + "global_fetch_strictly_public" + ], + "observability": { "enabled": true, - // Spans are billed as observability events (free tier 200k/day); add a - // traces head_sampling_rate below 1 if span volume becomes a cost concern. "traces": { - "enabled": true, - }, + "enabled": true + } }, - // Smart Placement relocates only the `fetch` handler (cron + Workflow runs - // stay at the edge) toward external subrequests — Postgres via Hyperdrive, - // SEO APIs — not D1/KV/R2 bindings, so it's a DB no-op on the D1 default. - // Must live here: a dashboard-only toggle is wiped by `wrangler deploy`. + "placement": { - "mode": "smart", + "mode": "smart" }, + "workflows": [ - // The site-audit engine lives in the open-seo-audit aux worker - // (wrangler.audit.jsonc) — its memory spikes (multi-MB Lighthouse - // payloads, in-flight HTML) OOMed this worker's near-limit baseline - // heap. This is a cross-script binding for starting audits and reading - // instance status. { "name": "site-audit-workflow", "binding": "SITE_AUDIT_WORKFLOW", "class_name": "SiteAuditWorkflow", - "script_name": "open-seo-audit", + "script_name": "open-seo-audit" }, { "name": "rank-check-workflow", "binding": "RANK_CHECK_WORKFLOW", - "class_name": "RankCheckWorkflow", - }, + "class_name": "RankCheckWorkflow" + } ], - // Durable Object backing the onboarding strategy chat (Agents SDK - // AIChatAgent). One instance per project; messages persist in the DO's - // SQLite. SQLite-backed classes must be declared in `migrations` below for - // the wrangler/workerd surfaces (local dev, Docker self-host); Alchemy - // deploys derive SQLite backing on their own. + "durable_objects": { "bindings": [ { "name": "ONBOARDING_CHAT", - "class_name": "OnboardingChatAgent", + "class_name": "OnboardingChatAgent" }, - // SAM in-app agent. One instance per chat session; messages persist in the - // DO's SQLite. SQLite-backed classes must be declared in `migrations`. { "name": "SAM_CHAT", - "class_name": "SamChatAgent", - }, - ], + "class_name": "SamChatAgent" + } + ] }, - // The site-audit engine lives in the open-seo-audit aux worker - // (wrangler.audit.jsonc), including the scratchpad DO — the DO cannot be - // bound cross-script here (the Cloudflare API rejects deleting the class - // below while any binding still references its name). Cancel + GDPR - // erasure go through this service binding's AuditEngine entrypoint. + "services": [ { "binding": "AUDIT_ENGINE", - "service": "open-seo-audit", - }, + "service": "open-seo-audit" + } ], + "migrations": [ { "tag": "v1", - "new_sqlite_classes": ["OnboardingChatAgent"], + "new_sqlite_classes": [ + "OnboardingChatAgent" + ] }, { "tag": "v2", - "new_sqlite_classes": ["SamChatAgent"], + "new_sqlite_classes": [ + "SamChatAgent" + ] }, { "tag": "v3", - "new_sqlite_classes": ["AuditScratchpad"], + "new_sqlite_classes": [ + "AuditScratchpad" + ] }, - // AuditScratchpad moved to the open-seo-audit worker (no binding may - // still reference the class name in the same upload — see the services - // comment above). Deleting it here is safe: scratchpad state is - // disposable by design (destroyed at finalize, 7-day cleanup alarm) — - // in-flight audits at the cutover fail once and the stale-audit - // reconciler reaps them. { "tag": "v4", - "deleted_classes": ["AuditScratchpad"], - }, + "deleted_classes": [ + "AuditScratchpad" + ] + } ], + "triggers": { - // Every 5 min: rank checks + stale-audit reconcile. Daily: OAuth KV GC. - // Dispatched on controller.cron in src/server.ts `scheduled`. - "crons": ["*/5 * * * *", "17 3 * * *"], + "crons": [ + "*/5 * * * *", + "17 3 * * *" + ] }, - // This config serves local dev and Docker self-host only. All Cloudflare - // deployments — previews, prod, self-host — go through Alchemy - // (alchemy.run.ts), which provisions real resources per stage and never - // reads these ids. - // - // The ids below are NOT dead config: miniflare derives its on-disk storage - // filenames (.wrangler/state, a persistent volume in Docker self-hosts) - // from an HMAC of `id`/`database_id`. Changing them orphans every existing - // local and self-hosted database. Leave them as-is. + "kv_namespaces": [ { "binding": "KV", - "id": "4abd52f3f2c549ac83cc2cb4ceec8620", + "id": "4abd52f3f2c549ac83cc2cb4ceec8620" }, { "binding": "OAUTH_KV", - "id": "bd1759494309474a9b423b029967b0db", - }, + "id": "bd1759494309474a9b423b029967b0db" + } ], + "d1_databases": [ { "binding": "DB", "database_name": "open-seo", "database_id": "37bee90a-e1aa-404f-b01e-b0d1d479bda1", - "migrations_dir": "drizzle", - }, + "migrations_dir": "drizzle" + } ], - // Postgres scale path (opt-in; D1 is the default). Hyperdrive is the ONLY way - // the app connects to Postgres — there is no direct-connection fallback. For - // local dev, `localConnectionString` is the throwaway Docker Postgres from - // docs/LOCAL_POSTGRES.md, and nothing connects to it unless - // DATABASE_PROVIDER=postgres is set in .env.local. - // - // Kept commented out: the id lives in OpenSEO's account, and only local - // Postgres dev reads this block (Alchemy deploys and the Docker image never - // do). Uncomment when running local Postgres dev per docs/LOCAL_POSTGRES.md. - // "hyperdrive": [ - // { - // "binding": "HYPERDRIVE", - // "id": "9d64ccfb559f44449ce52a143912f898", - // "localConnectionString": "postgres://openseo:openseo@localhost:5433/openseo", - // }, - // ], + + "hyperdrive": [ + { + "binding": "HYPERDRIVE", + "id": "local", + "localConnectionString": "postgres://openseo_user:Metatron2026@localhost:5432/openseo" + } + ], + "r2_buckets": [ { "bucket_name": "open-seo", - "binding": "R2", - }, - ], + "binding": "R2" + } + ] }