Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .agents/skills/testing-lighthouse/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,49 @@ The Lighthouse Scanner lives at `/lighthouse` in the Next.js app (`apps/web`). I
- **Cloudflare Pages preview**: Every PR gets a preview deploy. Check the PR comments from `cloudflare-workers-and-pages[bot]` for the preview URL (format: `https://<hash>.dba-92r.pages.dev`).
- **Production**: `https://designedbyanthony.com/lighthouse`

## Important: Pages vs Worker Deployment

This repo has **two separate deploy targets**:
- **Cloudflare Pages** (`apps/web`) — deploys automatically via PR/merge CI
- **Cloudflare Worker** (`apps/api`) — deploys separately via `wrangler deploy` from `apps/api/`

Cloudflare Pages preview URLs only include frontend changes. **API Worker changes (routes in `apps/api/src/routes/`) are NOT included in Pages previews.** To test API changes, use local `wrangler dev` (see below) or wait for a separate Worker deployment.

## Testing API Routes Locally

To test API Worker routes (e.g., `/api/report/:id/pdf`, `/api/audit`, `/api/audit/email-summary`):

1. **Start the API locally:**
```bash
cd apps/api && npx wrangler dev --local
```
This starts the Worker on `http://localhost:8788` with local KV.

2. **Seed test data into local KV** (needed for endpoints that read from the report store):
```bash
cd apps/api && npx wrangler kv key put "DBA-TEST1234" \
--binding AUDIT_REPORTS_KV --local \
"$(cat /path/to/test-report.json)"
```
The test report JSON must match the full stored payload structure from `audit.ts` — including nested `sitewide.robotsTxt.exists`, `sitewide.sitemap.exists`, `backlinks.found`, `places.found`, `indexCoverage.found` fields. Empty objects (`{}`) for these fields will cause `buildAuditPdf` to crash with "Cannot read properties of undefined".

3. **Verify with curl:**
```bash
# Health check
curl -s http://localhost:8788/health

# Test PDF headers
curl -sD - http://localhost:8788/api/report/DBA-TEST1234/pdf -o /tmp/test.pdf

# Test CORS preflight
curl -sD - -X OPTIONS http://localhost:8788/api/audit/email-summary \
-H "Origin: https://designedbyanthony.com" -o /dev/null
```

## Elysia Response Pattern

When testing Elysia routes that return raw `Response` objects: in Elysia, `set.headers` is ignored when returning a raw `Response`. Headers must be passed via the `Response` constructor. If you see missing headers on a raw Response return, check for this pattern mismatch. Routes that return plain objects (JSON) can use `set.headers` normally.

## Key Visual Elements to Verify

When testing Lighthouse UI changes, compare against the main site homepage (`/`) for consistency:
Expand All @@ -33,6 +76,7 @@ When testing Lighthouse UI changes, compare against the main site homepage (`/`)
- **CSS class name mismatches**: When refactoring CSS class names, always check that components still reference the correct names. Use `grep -oP 'className="[^"]*lh-[^"]*"' <component>.tsx | grep -oP 'lh-[a-z0-9_-]+'` to extract class names from components and verify they exist in the CSS file.
- **Local dev 500 errors**: The homepage might 500 locally due to missing env vars, but `/lighthouse` usually works. Use the Cloudflare Pages preview URL as a reliable alternative.
- **Bronze is intentional in some places**: The submit button CTA and decorative accent rules intentionally keep bronze — don't flag these as bugs.
- **Empty nested objects in test data**: When seeding test reports for PDF generation, ensure all nested objects (`sitewide`, `backlinks`, `places`, `indexCoverage`) have their full expected structure. `buildAuditPdf` accesses nested properties like `sitewide.robotsTxt.exists` and will crash on empty objects.

## Devin Secrets Needed

Expand Down
11 changes: 10 additions & 1 deletion STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,23 @@

> **Note:** This file tracks migration and release notes for the **Turborepo Cloudflare** app (`apps/web` Next.js Pages + `apps/api` Elysia Worker, `bun`, `bun.lock`). Older single-app / Astro-era detail is archived below for context — see [README.md](README.md) and [AGENTS.md](AGENTS.md).

## Codebase cleanup, perf optimization & tech integrations (2026-04-30)

- Removed Zustand badge from the "Our Edge" stack display — dependency was previously removed but badge remained.
- Removed stray `console.warn` in the competitor-scan catch block of `apps/api/src/routes/audit.ts`.
- Cleaned up outdated Firebase/Firestore references in `lighthouse2.md` (now reflects Cloudflare Pages/Workers + KV/D1).
- Added `manifest.webmanifest` to `apps/web/public/` with PWA metadata — triggers PWA detection on Wappalyzer/BuiltWith.
- Added JSON-LD structured data (`Organization`, `LocalBusiness`, `WebSite`) in root layout — zero render cost, boosts SEO.
- Verification: `bun run lint`, `bun run build` pass from the repo root.

## Lockfile fix + backend PDF offload (2026-04-30)


- **Lockfile fix (P0):** Removed conflicting workspace-level `overrides` from `apps/web/package.json` (stale `postcss ^8.4.38` vs root `^8.5.12`, duplicate `uuid`, unrecorded `yaml@<2.8.3`). Moved `yaml@<2.8.3` override to root `package.json`. Regenerated `bun.lock` — `bun install --frozen-lockfile` now passes in CI.
- **Dependency hygiene:** Relocated heavy backend-only packages (`@google/genai`, `googleapis`, `cheerio`, `jspdf`) from `apps/web` to `packages/shared` where they are actually imported. Frontend bundle no longer ships these libraries.
- **PDF offload to API Worker:** Created `GET /api/report/:id/pdf` on the ElysiaJS Worker (`apps/api/src/routes/reportPdf.ts`). The route fetches the persisted report from the KV store, calls `buildAuditPdf` server-side, and streams the PDF back with `Content-Disposition: attachment`. `AuditResults.tsx` now fetches from this endpoint instead of importing `jspdf` client-side — removes ~250 KB from the browser bundle and eliminates client-side PDF generation latency.
- Added `./lighthouse/*` export to `packages/shared` so `AuditData` types are accessible from any workspace.
- Verification: `bun install --frozen-lockfile`, `bun run build`, `bun run typecheck`, and `bun run lint` pass from the repo root.

## Micro SaaS store /tools page build-out (2026-04-30)

- Replaced the "Coming Soon" waitlist on `/tools` with a full product catalog showcasing 6 Stripe-backed micro-SaaS products: SiteScan, ReviewPilot, ClientHub, LocalRank, TestiFlow, and ContentMill.
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/routes/audit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,8 +282,8 @@ export const auditRoute = new Elysia({ aot: false }).post(
placesData.primaryType,
3,
);
} catch (compErr) {
console.warn("Competitor scan failed:", compErr);
} catch {
/* competitor scan is best-effort — swallow errors silently */
}

const categories = lighthouse.categories;
Expand Down
26 changes: 26 additions & 0 deletions apps/web/public/manifest.webmanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "Designed by Anthony",
"short_name": "DBA",
"description": "Custom web design and local SEO for service businesses in the Mohawk Valley and Central New York.",
"start_url": "/",
"display": "standalone",
"background_color": "#0f1218",
"theme_color": "#0f1218",
"icons": [
{
"src": "/site-icon-48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "/site-icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/site-icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
5 changes: 5 additions & 0 deletions apps/web/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import "@/design-system/dba-global.css";
import { CrispBootstrap } from "@/components/CrispBootstrap";
import { JsonLd } from "@/components/JsonLd";
import "@/styles/layout-shell.css";
import type { Metadata, Viewport } from "next";
import type { ReactNode } from "react";
Expand All @@ -25,6 +26,7 @@ export const metadata: Metadata = {
},
description:
"Custom web design and local SEO for service businesses in the Mohawk Valley and Central New York.",
manifest: "/manifest.webmanifest",
appleWebApp: {
capable: true,
statusBarStyle: "black-translucent",
Expand Down Expand Up @@ -77,6 +79,9 @@ export default function RootLayout({ children }: { children: ReactNode }) {
data-scroll-behavior="smooth"
data-lead-webhook={leadWebhookDefault || undefined}
>
<head>
<JsonLd />
</head>
<body>
{/* Google Tag Manager (noscript) */}
<noscript>
Expand Down
25 changes: 25 additions & 0 deletions apps/web/src/components/JsonLd.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
buildBaseOrganizationSchema,
buildBaseWebsiteSchema,
} from "@/lib/seo";

const ENTRIES = [buildBaseOrganizationSchema(), buildBaseWebsiteSchema()];

export function JsonLd() {
return (
<>
{ENTRIES.map((entry) => {
const id =
typeof entry["@id"] === "string" ? entry["@id"] : JSON.stringify(entry);
return (
<script
key={id}
type="application/ld+json"
// biome-ignore lint/security/noDangerouslySetInnerHtml: structured data requires raw JSON injection
dangerouslySetInnerHTML={{ __html: JSON.stringify(entry) }}
/>
);
})}
</>
);
}
1 change: 0 additions & 1 deletion apps/web/src/components/marketing/EnrichedPages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,6 @@ export function OurEdgePage() {
href: "https://deepmind.google/technologies/gemini/",
},
{ label: "Zod", href: "https://zod.dev" },
{ label: "Zustand", href: "https://zustand.docs.pmnd.rs" },
].map(({ label, href }) => (
<a
key={label}
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/styles/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -1773,7 +1773,7 @@ body:not(.has-progress-bar) #reading-progress-bar {

@media (max-width: 600px) {
.salesforce-form-actions {
justify-content: stretch;
justify-content: center;
}

.salesforce-form-actions .btn {
Expand Down
4 changes: 2 additions & 2 deletions lighthouse2.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Lighthouse Scanner — product description & operator guide

This document is the **single source of truth** for how we **describe** the scanner (marketing, OG tags, splash copy) and what **you** configure in **Firebase App Hosting** / env. Structure is inspired by common open-source SEO audit READMEs (for example [vchaitanyachowdari/seo-audits-generator](https://github.com/vchaitanyachowdari/seo-audits-generator)) — clear feature blocks, stack, env, usage — adapted to **this repo’s real implementation**, not a generic SaaS template.
This document is the **single source of truth** for how we **describe** the scanner (marketing, OG tags, splash copy) and what **you** configure in **Cloudflare Pages / Workers** env. Structure is inspired by common open-source SEO audit READMEs (for example [vchaitanyachowdari/seo-audits-generator](https://github.com/vchaitanyachowdari/seo-audits-generator)) — clear feature blocks, stack, env, usage — adapted to **this repo’s real implementation**, not a generic SaaS template.

---

Expand All @@ -21,7 +21,7 @@ This document is the **single source of truth** for how we **describe** the scan
| **Authority / links** | “Backlink snapshot when configured” | **Moz API** when `MOZ_API_CREDENTIALS` / token is set; otherwise omit or show “not configured” in UI |
| **Conversion / UX** | “Trust + conversion lens” | AI **conversionScore** + CTA/schema/tel/form signals from HTML |
| **AI report** | Plain-English executive summary + top fixes | **Gemini** (`generateAiInsight`) with fallback |
| **Persistence** | Sharable report when storage works | Firestore report store when configured |
| **Persistence** | Sharable report when storage works | KV / D1 report store when configured |
| **CRM / leads** | Optional | Convex webhooks, Sheets, Gmail — see `.env.example` (`AUDIT_LEAD_WEBHOOK_*`, `LEAD_WEBHOOK_URL`) |

Do **not** claim: unlimited full-site crawl like Semrush/Ahrefs, JS-rendered crawl of every route, or “Moz DA” unless Moz is configured.
Expand Down