From 3e489d0771adda8c71d266735701d79b9955ddfe Mon Sep 17 00:00:00 2001 From: Ben Senescu <44480372+bensenescu@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:36:16 -0400 Subject: [PATCH] feat: remove Every App dependency (#8) * remove Every App SDK and add auth modes for Cloudflare Access and local_noauth * align local dev auth defaults and normalize Access team domain * Apply suggestions from code review * restore local drizzle D1 URL helper * save * fix auth error mapping and document self-hosting setup * Tweak readme * improve auth config error UI and remove manifest link * fix team domain config validation and docs anchor --- .env.example | 43 ++--- README.md | 88 +++++++++- SELF_HOSTING_DOCKER.md | 11 +- compose.yaml | 5 +- every-app.jsonc | 5 - knip.jsonc | 2 +- opencode.jsonc | 4 - package.json | 26 ++- src/client/components/AuthConfigErrorCard.tsx | 52 ++++++ .../components/DefaultCatchBoundary.tsx | 21 ++- src/client/components/Sidebar.tsx | 16 +- src/client/lib/error-messages.ts | 7 + src/env.d.ts | 5 +- src/middleware/ensureUser.ts | 161 +++++++++++++++--- src/routes/__root.tsx | 55 +++--- src/routes/index.tsx | 40 ++++- src/routes/p/$projectId/route.tsx | 7 +- src/serverFunctions/middleware.ts | 2 - src/shared/error-codes.ts | 1 + src/types/vite-env.d.ts | 2 - vite.config.ts | 2 +- worker-configuration.d.ts | 10 +- wrangler.jsonc | 12 +- 23 files changed, 422 insertions(+), 155 deletions(-) delete mode 100644 every-app.jsonc create mode 100644 src/client/components/AuthConfigErrorCard.tsx diff --git a/.env.example b/.env.example index bff168a..26073fa 100644 --- a/.env.example +++ b/.env.example @@ -1,31 +1,22 @@ -# Every App / Gateway settings -VITE_APP_ID=open-seo - -# ------------------------------------------------------------- -# # --- Local development -- -# # Run everyapp app setup-local to automatically configure you .env.local if you've set up your gateway in cloudflare -# # This should be: https://every-app-gateway. -# VITE_GATEWAY_URL= -# GATEWAY_URL= - -# # See README.md for instructions for how to get this. +# ----------------------------------------------------------------------------- +# Core settings +# ----------------------------------------------------------------------------- +# Required for all modes. See README.md for setup instructions. # DATAFORSEO_API_KEY= -# # Choose local app port -# # PORT=3001 - -# ------------------------------------------------------------- -# --- Docker self-hosting only (local mode) --- -# See SELF_HOSTING_DOCKER.md -# Docker compose runs with BYPASS_GATEWAY_LOCAL_ONLY=true. -# Optional host/container port (defaults to 3001): +# Optional app port # PORT=3001 -# Optional in .env because compose already sets it. -# BYPASS_GATEWAY_LOCAL_ONLY=true -# Optional override (defaults to open-seo): -# VITE_APP_ID=open-seo -# # See README.md for instructions for how to get this. -# DATAFORSEO_API_KEY= +# ----------------------------------------------------------------------------- +# Auth mode +# ----------------------------------------------------------------------------- +# - cloudflare_access: validate Cloudflare Access JWTs (recommended for deploys) +# - local_noauth: local trusted mode with injected admin user (admin@localhost) +# - hosted: reserved for future hosted auth flow (not implemented yet) +# +# Defaults to cloudflare_access when unset. +# AUTH_MODE=cloudflare_access -# Legacy gateway auth keys are not needed in Docker self-host mode. +# Required when AUTH_MODE=cloudflare_access +# TEAM_DOMAIN=https://your-team.cloudflareaccess.com +# POLICY_AUD=your-cloudflare-access-aud-tag diff --git a/README.md b/README.md index 2399487..ee08f44 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ OpenSEO is an SEO tool for _the people_. If tools like Semrush or Ahrefs are too ![OpenSEO demo (placeholder)](https://github.com/user-attachments/assets/6a928771-66ff-486b-b131-a54a3943985f) +[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/every-app/open-seo) + ## Table of Contents - [Why Use This](#why-use-this) @@ -12,7 +14,9 @@ OpenSEO is an SEO tool for _the people_. If tools like Semrush or Ahrefs are too - [Community](#community) - [Pricing / Costs (Free + API costs)](#pricing--costs) - [DataForSEO API Key Setup](#dataforseo-api-key-setup) -- [Docker Self Hosting](#docker-self-hosting) +- [Self-hosting](#self-hosting) + - [Cloudflare Deployment + Access Setup](#cloudflare-deployment--access-setup) + - [Docker Self Hosting](#docker-self-hosting) - [Local Development](#local-development) - [Contributing](#contributing) - [SEO API Cost Reference](#seo-api-cost-reference) @@ -80,6 +84,73 @@ printf '%s' 'YOUR_LOGIN:YOUR_PASSWORD' | base64 - Docker self-hosting: `.env` - Local development: `.env.local` +## Self-hosting + +OpenSEO supports two self-hosting paths: + +- Cloudflare for hosting on the internet (Recommended). +- Docker for your homelab or local use. + +Use this quick guide: + +- Choose Cloudflare when: + - You've never used Docker before. + - You want a more SaaS like experience. + - You want to use it from multiple devices or with teammates. + - You want support for more powerful features in the future like sharing public links to reports or site audits rendering your websites javascript. +- Choose Docker when: + - You already have Docker installed and want to get setup most quickly. + - You have a homelab setup. + - You only want to use OpenSEO locally on one device. + +## Cloudflare Deployment + Access Setup + +You can use the Deploy button at the top of this README or run with Wrangler directly. + +### 1) Deploy the Worker + +Clicking this button will open a page to deploy OpenSEO in your Cloudflare account. + +[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/every-app/open-seo) + +### 2) Set required environment variables + +OpenSEO needs these vars in production: + +- `DATAFORSEO_API_KEY`: base64-encoded `login:password` from DataForSEO. +- `AUTH_MODE=cloudflare_access` +- `TEAM_DOMAIN`: your Access team domain (for example `https://your-team.cloudflareaccess.com`). +- `POLICY_AUD`: Access Application Audience tag for your OpenSEO app. + +You can set plain vars and secrets with Wrangler: + +```sh +pnpm exec wrangler secret put DATAFORSEO_API_KEY +pnpm exec wrangler secret put POLICY_AUD +pnpm exec wrangler secret put TEAM_DOMAIN +pnpm exec wrangler secret put AUTH_MODE +``` + +You can also set these in Cloudflare Dashboard under Worker Settings. + +### 3) Configure Cloudflare Access + +In Cloudflare Zero Trust: + +1. Create an Access application for your OpenSEO route/domain. +2. Add policy rules for the identities/groups allowed to access OpenSEO. +3. Ensure requests to your Worker include `cf-access-jwt-assertion`. +4. Copy values into OpenSEO config: + - Access team domain -> `TEAM_DOMAIN` + - Access app AUD tag -> `POLICY_AUD` + +### 4) Validate setup + +- Visit your OpenSEO URL. +- You should be prompted by Cloudflare Access if not signed in. +- After sign-in, the app should load normally. +- If config is missing, OpenSEO shows an in-app setup error that links back to this section. + ## Docker Self Hosting Quickstart: @@ -113,11 +184,11 @@ cp .env.example .env.local pnpm install # Initialize local DB schema (required on a fresh machine) pnpm run db:migrate:local -# This runs in BYPASS_GATEWAY mode for local use and quick testing. +# This runs in local_noauth mode for local use and quick testing. pnpm dev:agents ``` -`pnpm dev` runs on `http://localhost:3001` by default (or `PORT` from `.env.local`). +`pnpm dev` runs on `http://localhost:3001` by default (or `PORT` from `.env.local`) in `AUTH_MODE=local_noauth`. `pnpm dev:agents` runs through [portless](https://github.com/vercel-labs/portless) at `http://open-seo.localhost:1355` by default. @@ -140,6 +211,17 @@ pnpm dev:agents - `pnpm dev:agents` mirrors output to `.logs/dev-server.log` (gitignored). - The log file is overwritten on each run. +### Auth modes + +- `AUTH_MODE=cloudflare_access` (default): validates Cloudflare Access JWTs (`cf-access-jwt-assertion`) using `TEAM_DOMAIN` + `POLICY_AUD`. +- `AUTH_MODE=local_noauth`: local trusted mode, no auth check, injects `admin@localhost`. +- `AUTH_MODE=hosted`: reserved for upcoming multi-tenant auth flow (not yet implemented). + +Local scripts (`pnpm dev` and `pnpm dev:agents`) set `AUTH_MODE=local_noauth` automatically. +Use `AUTH_MODE=cloudflare_access pnpm dev` when you specifically want to test Access validation locally. + +For Cloudflare deployments, ensure Cloudflare Access is enabled on your Worker route/domain and provide `TEAM_DOMAIN` + `POLICY_AUD` in environment variables. + ### Database Commands Generate migration: diff --git a/SELF_HOSTING_DOCKER.md b/SELF_HOSTING_DOCKER.md index bc0581e..13be8c1 100644 --- a/SELF_HOSTING_DOCKER.md +++ b/SELF_HOSTING_DOCKER.md @@ -1,8 +1,8 @@ # Docker Self-Hosting -This guide runs OpenSEO as a local service without Every App Gateway. +This guide runs OpenSEO as a local service. -In this mode, OpenSEO runs with `BYPASS_GATEWAY_LOCAL_ONLY=true`, so authentication and Gateway-managed user accounts are disabled. +In this mode, OpenSEO runs with `AUTH_MODE=local_noauth`, so request authentication is disabled and a local admin user (`admin@localhost`) is injected automatically. ## Prerequisites @@ -13,8 +13,8 @@ In this mode, OpenSEO runs with `BYPASS_GATEWAY_LOCAL_ONLY=true`, so authenticat This stack is local-first and uses dev runtimes to emulate Cloudflare Worker bindings. - Do not expose these ports directly to the public internet. -- There is no built-in Gateway auth in this mode. -- If you expose it beyond localhost, put it behind the same authentication layer you use for your other self-hosted services (or use the [Cloudflare deployment path](./README.md#self-hosting-deploy-on-cloudflare-5-10-minutes)). +- There is no built-in auth check in this mode. +- If you expose it beyond localhost, put it behind the same authentication layer you use for your other self-hosted services (or use the [Cloudflare deployment path](./README.md#cloudflare-deployment--access-setup)). ## 1) Configure env values @@ -33,8 +33,7 @@ Required: Optional: - `PORT` (defaults to `3001`) -- `VITE_APP_ID` (defaults to `open-seo`) -- `BYPASS_GATEWAY_LOCAL_ONLY=true` (Docker compose already sets this) +- `AUTH_MODE=local_noauth` (Docker compose already sets this) ## 2) Start OpenSEO diff --git a/compose.yaml b/compose.yaml index 1b5231f..ee5031a 100644 --- a/compose.yaml +++ b/compose.yaml @@ -6,10 +6,7 @@ services: working_dir: /app environment: - PORT=${PORT:-3001} - - BYPASS_GATEWAY_LOCAL_ONLY=true - - VITE_APP_ID=${VITE_APP_ID:-open-seo} - - VITE_GATEWAY_URL=${VITE_GATEWAY_URL:-http://localhost:${PORT:-3001}} - - GATEWAY_URL=${GATEWAY_URL:-http://localhost:${PORT:-3001}} + - AUTH_MODE=local_noauth - DATAFORSEO_API_KEY=${DATAFORSEO_API_KEY} - VITE_SHOW_DEVTOOLS=false command: diff --git a/every-app.jsonc b/every-app.jsonc deleted file mode 100644 index d10ab98..0000000 --- a/every-app.jsonc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "appId": "open-seo", - "displayName": "OpenSEO", - "description": "Own your SEO. Research keywords and competitors on your terms.", -} diff --git a/knip.jsonc b/knip.jsonc index a63d875..5916d00 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -1,5 +1,5 @@ { - "ignoreBinaries": ["everyapp"], + "ignoreBinaries": [], "entry": [ // Detect Tanstack Start Routes "src/router.tsx", diff --git a/opencode.jsonc b/opencode.jsonc index 3f15cc0..f79accb 100644 --- a/opencode.jsonc +++ b/opencode.jsonc @@ -5,9 +5,5 @@ "type": "local", "command": ["npx", "-y", "@upstash/context7-mcp"], }, - "every-app": { - "type": "local", - "command": ["npx", "-y", "@every-app/mcp"], - }, }, } diff --git a/package.json b/package.json index 27dbb15..c24badc 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "sideEffects": false, "type": "module", "scripts": { - "dev": "vite dev", - "dev:agents": "mkdir -p .logs && BYPASS_GATEWAY_LOCAL_ONLY=true portless run vite dev 2>&1 | tee .logs/dev-server.log", + "dev": "AUTH_MODE=local_noauth vite dev", + "dev:agents": "mkdir -p .logs && AUTH_MODE=local_noauth portless run vite dev 2>&1 | tee .logs/dev-server.log", "build": "vite build && tsc --noEmit", "lint": "oxlint .", "lint:fix": "oxlint . --fix", @@ -16,13 +16,27 @@ "format:check": "prettier --check .", "format:write": "prettier . --write", "db:generate": "drizzle-kit generate", - "db:migrate:local": "drizzle-kit migrate", - "db:migrate:prod": "npx everyapp app remote-d1-shell -- drizzle-kit migrate --config=drizzle-prod.config.ts", - "db:studio:local": "drizzle-kit studio", - "db:studio:prod": "npx everyapp app remote-d1-shell -- drizzle-kit studio --config=drizzle-prod.config.ts", + "db:migrate:local": "wrangler d1 migrations apply DB --local", + "db:migrate:prod": "wrangler d1 migrations apply DB --remote", "knip": "knip", "ci": "prettier --check . && knip && tsc --noEmit && oxlint ." }, + "cloudflare": { + "bindings": { + "AUTH_MODE": { + "description": "Set to `cloudflare_access` for secured deployments behind Cloudflare Access, or `local_noauth` for trusted local self-host mode only." + }, + "TEAM_DOMAIN": { + "description": "Cloudflare Access team domain (for example `https://your-team.cloudflareaccess.com`) used to validate `cf-access-jwt-assertion` JWTs." + }, + "POLICY_AUD": { + "description": "Cloudflare Access Application Audience (AUD) tag for this Worker route/domain." + }, + "DATAFORSEO_API_KEY": { + "description": "Base64-encoded `login:password` for DataForSEO API access." + } + } + }, "dependencies": { "@every-app/sdk": "^0.1.13", "@tanstack/query-core": "^5.90.9", diff --git a/src/client/components/AuthConfigErrorCard.tsx b/src/client/components/AuthConfigErrorCard.tsx new file mode 100644 index 0000000..f08989d --- /dev/null +++ b/src/client/components/AuthConfigErrorCard.tsx @@ -0,0 +1,52 @@ +import { ShieldAlert } from "lucide-react"; + +const README_CLOUDFLARE_ACCESS_URL = + "https://github.com/every-app/open-seo#cloudflare-deployment--access-setup"; + +type AuthConfigErrorCardProps = { + message: string; + onRetry?: () => void; +}; + +export function AuthConfigErrorCard({ + message, + onRetry, +}: AuthConfigErrorCardProps) { + return ( +
+
+

+ + Cloudflare Access setup required +

+ +
+ {message} +
+ +

+ This deployment is missing required Access settings for + AUTH_MODE=cloudflare_access. Configure{" "} + TEAM_DOMAIN and + POLICY_AUD, then retry. +

+ +
+ {onRetry ? ( + + ) : null} + + Open Setup Guide + +
+
+
+ ); +} diff --git a/src/client/components/DefaultCatchBoundary.tsx b/src/client/components/DefaultCatchBoundary.tsx index 5ac5514..96f27a9 100644 --- a/src/client/components/DefaultCatchBoundary.tsx +++ b/src/client/components/DefaultCatchBoundary.tsx @@ -1,6 +1,10 @@ import { Link, rootRouteId, useMatch, useRouter } from "@tanstack/react-router"; import type { ErrorComponentProps } from "@tanstack/react-router"; -import { getStandardErrorMessage } from "@/client/lib/error-messages"; +import { + getErrorCode, + getStandardErrorMessage, +} from "@/client/lib/error-messages"; +import { AuthConfigErrorCard } from "@/client/components/AuthConfigErrorCard"; export function DefaultCatchBoundary({ error }: ErrorComponentProps) { const router = useRouter(); @@ -13,6 +17,21 @@ export function DefaultCatchBoundary({ error }: ErrorComponentProps) { error, "Something went wrong. Please try again.", ); + const errorCode = getErrorCode(error); + const showAuthConfigHelp = errorCode === "AUTH_CONFIG_MISSING"; + + if (showAuthConfigHelp) { + return ( +
+ { + router.invalidate(); + }} + /> +
+ ); + } return (
diff --git a/src/client/components/Sidebar.tsx b/src/client/components/Sidebar.tsx index f93a1ec..eb02b5a 100644 --- a/src/client/components/Sidebar.tsx +++ b/src/client/components/Sidebar.tsx @@ -21,13 +21,7 @@ export function Sidebar({ return (
- - Every App - + OpenSEO {onClose && ( - - Every App - + OpenSEO
- {/* Desktop: EveryApp brand + nav links (left) */} + {/* Desktop: app brand + nav links (left) */}
- - Every App - + + OpenSEO + {projectId && projectNavItems.map((item) => { const Icon = item.icon; @@ -195,28 +187,23 @@ function RootDocument({ children }: { children: React.ReactNode }) { - - <> - {children} - + {children} + + {showDevtools ? ( + , + defaultOpen: true, + }, + ]} /> - {showDevtools ? ( - , - defaultOpen: true, - }, - ]} - /> - ) : null} - - + ) : null} + diff --git a/src/routes/index.tsx b/src/routes/index.tsx index e54e36f..9f38d08 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -2,7 +2,11 @@ import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { useEffect } from "react"; import { useMutation } from "@tanstack/react-query"; import { getOrCreateDefaultProject } from "@/serverFunctions/keywords"; -import { getStandardErrorMessage } from "@/client/lib/error-messages"; +import { + getErrorCode, + getStandardErrorMessage, +} from "@/client/lib/error-messages"; +import { AuthConfigErrorCard } from "@/client/components/AuthConfigErrorCard"; export const Route = createFileRoute("/")({ component: IndexRedirect, @@ -26,14 +30,34 @@ function IndexRedirect() { }, [mutate]); if (isError) { + const errorCode = getErrorCode(error); + + if (errorCode === "AUTH_CONFIG_MISSING") { + return ( +
+ { + mutate(); + }} + /> +
+ ); + } + return ( -
-

- {getStandardErrorMessage( - error, - "An unexpected error occurred. Please check server logs.", - )} -

+
+
+

+ {getStandardErrorMessage( + error, + "An unexpected error occurred. Please check server logs.", + )} +

+
); } diff --git a/src/routes/p/$projectId/route.tsx b/src/routes/p/$projectId/route.tsx index 1d6e71e..25ef767 100644 --- a/src/routes/p/$projectId/route.tsx +++ b/src/routes/p/$projectId/route.tsx @@ -1,7 +1,6 @@ import { createFileRoute, Outlet, useNavigate } from "@tanstack/react-router"; import { useEffect } from "react"; import { useQuery } from "@tanstack/react-query"; -import { useCurrentUser } from "@every-app/sdk/tanstack"; import { getProject } from "@/serverFunctions/keywords"; export const Route = createFileRoute("/p/$projectId")({ @@ -12,7 +11,6 @@ function ProjectLayout() { const params = Route.useParams(); const { projectId } = params; const navigate = useNavigate(); - const user = useCurrentUser(); const { data: project, @@ -21,14 +19,13 @@ function ProjectLayout() { } = useQuery({ queryKey: ["project", projectId], queryFn: () => getProject({ data: { projectId } }), - enabled: user !== null, }); useEffect(() => { - if (user !== null && !isLoading && (isError || !project)) { + if (!isLoading && (isError || !project)) { void navigate({ to: "/" }); } - }, [isLoading, isError, project, navigate, user]); + }, [isLoading, isError, project, navigate]); if (isLoading) { return ( diff --git a/src/serverFunctions/middleware.ts b/src/serverFunctions/middleware.ts index 4ee03da..16d6693 100644 --- a/src/serverFunctions/middleware.ts +++ b/src/serverFunctions/middleware.ts @@ -1,9 +1,7 @@ -import { useSessionTokenClientMiddleware } from "@every-app/sdk/tanstack"; import { errorHandlingMiddleware } from "@/middleware/errorHandling"; import { ensureUserMiddleware } from "@/middleware/ensureUser"; export const authenticatedServerFunctionMiddleware = [ errorHandlingMiddleware, - useSessionTokenClientMiddleware, ensureUserMiddleware, ] as const; diff --git a/src/shared/error-codes.ts b/src/shared/error-codes.ts index fca310d..b4584d9 100644 --- a/src/shared/error-codes.ts +++ b/src/shared/error-codes.ts @@ -1,5 +1,6 @@ export const ERROR_CODES = [ "UNAUTHENTICATED", + "AUTH_CONFIG_MISSING", "FORBIDDEN", "NOT_FOUND", "VALIDATION_ERROR", diff --git a/src/types/vite-env.d.ts b/src/types/vite-env.d.ts index 1404692..774a947 100644 --- a/src/types/vite-env.d.ts +++ b/src/types/vite-env.d.ts @@ -6,8 +6,6 @@ interface ViteTypeOptions { } interface ImportMetaEnv { - readonly VITE_GATEWAY_URL: string; - readonly VITE_APP_ID: string; readonly VITE_SHOW_DEVTOOLS?: string; // more env variables... } diff --git a/vite.config.ts b/vite.config.ts index 427e5d5..372a2ca 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -12,7 +12,7 @@ export default defineConfig(({ mode }) => { const showDevtools = env.VITE_SHOW_DEVTOOLS !== "false"; return { - envPrefix: ["VITE_", "BYPASS_GATEWAY_LOCAL_ONLY"], + envPrefix: ["VITE_"], server: { port, }, diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index 6064aa9..275ee46 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -// Generated by Wrangler by running `wrangler types` (hash: aac0d6d9ff3ca42b08edafb09451cfdf) +// Generated by Wrangler by running `wrangler types` (hash: 0e03c138742d121a834f2931f5295961) // Runtime types generated with workerd@1.20260219.0 2025-09-02 nodejs_compat declare namespace Cloudflare { interface GlobalProps { @@ -13,7 +13,11 @@ declare namespace Cloudflare { VITE_GATEWAY_URL: string; GATEWAY_URL: string; GATEWAY_APP_API_TOKEN: string; - EVERY_APP_GATEWAY: Fetcher /* every-app-gateway */; + DATAFORSEO_API_KEY: string; + PORT: string; + BETTER_AUTH_SECRET: string; + JWT_PRIVATE_KEY: string; + JWT_PUBLIC_KEY: string; SITE_AUDIT_WORKFLOW: Workflow[0]['payload']>; } } @@ -22,7 +26,7 @@ type StringifyValues> = { [Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string; }; declare namespace NodeJS { - interface ProcessEnv extends StringifyValues> {} + interface ProcessEnv extends StringifyValues> {} } // Begin runtime types diff --git a/wrangler.jsonc b/wrangler.jsonc index 1a7ed15..1c39463 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -1,6 +1,6 @@ { "$schema": "node_modules/wrangler/config-schema.json", - "name": "every-open-seo", + "name": "open-seo", "main": "src/server.ts", "compatibility_date": "2025-09-02", "compatibility_flags": ["nodejs_compat"], @@ -14,12 +14,6 @@ "class_name": "SiteAuditWorkflow", }, ], - "services": [ - { - "binding": "EVERY_APP_GATEWAY", - "service": "every-app-gateway", - }, - ], "kv_namespaces": [ { "binding": "KV", @@ -29,8 +23,8 @@ "d1_databases": [ { "binding": "DB", - "database_name": "every-open-seo", - "database_id": "dc7eb9f1-9a43-4d37-9bc9-1293a9200f13", + "database_name": "open-seo", + "database_id": "37bee90a-e1aa-404f-b01e-b0d1d479bda1", "migrations_dir": "drizzle", }, ],