03ae372509
Subagent 1 (Codex review pass): LandingPageClient.tsx - Guard GSAP .scroll-reveal loop to prevent 'missing target' warnings when no targets exist brands/page.tsx - Comment cleanup for selector scoping tuxedo/page.tsx - Add pageScopeRef + use as explicit GSAP context scope (fixes 'Invalid scope' warnings) - Reduced-motion guard for counter animations - Comments noting scope is now resilient HeroSection.tsx - Wrap sections in containerRef for proper GSAP context - Reduced-motion support (final states shown immediately, no heavy anims) - Counter animation: switched from textContent tween (unreliable, left values at 0) to proxy object pattern (val -> display) - Added IO + rAF fallback if GSAP is unavailable - Added section ids: #features, #stats, #reviews (matches nav anchors) - Reduced blank scroll region (300vh -> 140vh on story section) - Removed duplicate inline footer (LandingPageWrapper <Footer /> now sole source) LandingPageWrapper.tsx - Anchor comments noting ids added in HeroSection Docs + scripts: - docs/ADMIN_FUNCTIONALITY_CHECKLIST.md (admin app coverage notes) - docs/pricing-assessment.md (pricing model notes) - scripts/apply-admin-create-stop.js, check-stop-fns*.js, verify-stop-fns.js (admin stop creation tooling)
161 lines
6.8 KiB
JavaScript
Executable File
161 lines
6.8 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
/**
|
|
* apply-admin-create-stop.js
|
|
*
|
|
* Applies the admin_create_stop RPC using ONLY the keys in .env.local.
|
|
* You do NOT need Supabase dashboard / SQL editor access.
|
|
*
|
|
* This is the fix for the "could not find the function public.admin_create_stop(...)"
|
|
* error when you click "Add New Stop".
|
|
*
|
|
* Usage (run from the repo root on a machine that can reach the DB):
|
|
* node scripts/apply-admin-create-stop.js
|
|
*
|
|
* The script connects as the real postgres user (using your SUPABASE_SERVICE_ROLE_KEY
|
|
* as the password) and runs the CREATE OR REPLACE + GRANT + NOTIFY.
|
|
* This is the only reliable way to create the SECURITY DEFINER function because
|
|
* direct REST inserts are blocked by the `block_stops_mutations` policy.
|
|
*
|
|
* After it prints success:
|
|
* - Restart your `npm run dev`
|
|
* - "Add New Stop" (AddStopModal / NewStopForm) will now call the RPC successfully.
|
|
*/
|
|
|
|
const { Client } = require("pg");
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
|
|
// Load .env.local (same as the rest of the project)
|
|
try {
|
|
require("dotenv").config({ path: path.resolve(__dirname, "../.env.local") });
|
|
} catch (e) {
|
|
// dotenv might not be hoisted in some setups; the keys may already be in process.env
|
|
}
|
|
|
|
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
|
|
if (!supabaseUrl || !serviceKey) {
|
|
console.error("❌ Missing NEXT_PUBLIC_SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY in .env.local");
|
|
console.error(" Make sure you are running from the repo root and .env.local exists with the keys.");
|
|
process.exit(1);
|
|
}
|
|
|
|
// Derive the direct Postgres URL (same logic as supabase/push-migrations.js)
|
|
const projectRef = supabaseUrl.replace("https://", "").split(".")[0];
|
|
const pgHost = `db.${projectRef}.supabase.co`;
|
|
const dbUrl = `postgresql://postgres:${encodeURIComponent(serviceKey)}@${pgHost}:5432/postgres`;
|
|
|
|
console.log("🔧 apply-admin-create-stop RPC installer");
|
|
console.log(" Project:", projectRef);
|
|
console.log(" Using service role key from .env.local (full Postgres access)");
|
|
console.log("");
|
|
|
|
async function main() {
|
|
const migrationPath = path.resolve(__dirname, "../supabase/migrations/202_fix_admin_create_stop.sql");
|
|
|
|
if (!fs.existsSync(migrationPath)) {
|
|
console.error("❌ Cannot find migration file:", migrationPath);
|
|
console.error(" The file should have been created by the previous fix.");
|
|
process.exit(1);
|
|
}
|
|
|
|
const sql = fs.readFileSync(migrationPath, "utf8");
|
|
|
|
const client = new Client({
|
|
connectionString: dbUrl,
|
|
ssl: { rejectUnauthorized: false },
|
|
// Some environments need a longer timeout
|
|
connectionTimeoutMillis: 15000,
|
|
});
|
|
|
|
try {
|
|
console.log("⏳ Connecting to Postgres (this uses port 5432 outbound)...");
|
|
await client.connect();
|
|
console.log("✅ Connected.");
|
|
|
|
console.log("⏳ Executing 202_fix_admin_create_stop.sql (CREATE OR REPLACE + GRANT + NOTIFY)...");
|
|
const result = await client.query(sql);
|
|
|
|
console.log("✅ Function installed / updated successfully.");
|
|
console.log(" Rows affected in last statement:", result.rowCount ?? "(see output above if any)");
|
|
console.log("");
|
|
console.log("🎉 admin_create_stop is now available via the anon key (SECURITY DEFINER).");
|
|
|
|
// Quick verification using the anon key over HTTPS (this is what the app uses)
|
|
try {
|
|
const anon = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
const probeBody = {
|
|
p_active: false,
|
|
p_address: null,
|
|
p_brand_id: "00000000-0000-0000-0000-000000000000", // will FK fail, but proves the function exists
|
|
p_city: "verify",
|
|
p_cutoff_time: null,
|
|
p_date: "2099-12-31",
|
|
p_location: "verify",
|
|
p_state: "TS",
|
|
p_time: "10:00",
|
|
p_zip: null,
|
|
};
|
|
const probe = await fetch(`${process.env.NEXT_PUBLIC_SUPABASE_URL}/rest/v1/rpc/admin_create_stop`, {
|
|
method: "POST",
|
|
headers: { apikey: anon, "Content-Type": "application/json" },
|
|
body: JSON.stringify(probeBody),
|
|
});
|
|
const txt = await probe.text();
|
|
if (probe.status === 404) {
|
|
console.log("⚠️ Post-apply probe still got 404 — you may need to wait a few seconds or the NOTIFY didn't take.");
|
|
} else if (/foreign key|violates|brand_id/i.test(txt)) {
|
|
console.log("✅ Post-apply probe: function exists (got expected FK error on the zero-UUID brand — this is success).");
|
|
} else {
|
|
console.log("✅ Post-apply probe returned:", txt.slice(0, 200));
|
|
}
|
|
} catch (e) {
|
|
console.log(" (verification fetch skipped:", e.message, ")");
|
|
}
|
|
|
|
console.log("");
|
|
console.log("Next:");
|
|
console.log(" • Restart your dev server (`npm run dev`) if it was already running.");
|
|
console.log(" • Go to the stops admin page and try \"Add New Stop\".");
|
|
console.log(" • (Optional but recommended) node scripts/verify-stop-fns.js");
|
|
} catch (err) {
|
|
const msg = (err && err.message) ? err.message : String(err);
|
|
console.error("❌ Failed to apply the function.");
|
|
console.error(" Error:", msg.slice(0, 500));
|
|
|
|
if (/ENOTFOUND|EAI_AGAIN|getaddrinfo|network is unreachable|ECONNREFUSED|timeout/i.test(msg)) {
|
|
console.log("");
|
|
console.log("💡 Your current environment cannot reach the database on port 5432.");
|
|
console.log(" This is common inside some containers/CI sandboxes.");
|
|
console.log("");
|
|
console.log(" Copy and run ONE of the following on a *normal* laptop/terminal that has");
|
|
console.log(" outbound access to Supabase Postgres (most developer machines can reach it):");
|
|
console.log("");
|
|
console.log(" Option A (easiest, uses exactly the same code as this script):");
|
|
console.log(" npm run migrate:one 202");
|
|
console.log("");
|
|
console.log(" Option B (psql):");
|
|
console.log(" # Build the URL using the key from your .env.local (same as this script does):");
|
|
console.log(" # psql \"postgresql://postgres:PUT_YOUR_SERVICE_ROLE_KEY_HERE@db." + projectRef + ".supabase.co:5432/postgres\" \\");
|
|
console.log(" # -f supabase/migrations/202_fix_admin_create_stop.sql");
|
|
console.log("");
|
|
console.log(" Option C (Supabase CLI):");
|
|
console.log(" supabase db push --db-url \"postgresql://postgres:YOUR_SERVICE_ROLE_KEY@db." + projectRef + ".supabase.co:5432/postgres\" \\");
|
|
console.log(" supabase/migrations/202_fix_admin_create_stop.sql");
|
|
console.log("");
|
|
console.log(" After the command succeeds you will see the NOTIFY and the function will exist.");
|
|
console.log(" Then restart `npm run dev` and \"Add New Stop\" will stop showing the function-not-found error.");
|
|
}
|
|
|
|
process.exit(1);
|
|
} finally {
|
|
try { await client.end(); } catch {}
|
|
}
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error("Unexpected crash:", e);
|
|
process.exit(1);
|
|
});
|