feat(infra): add Square/Stripe OAuth complete routes, auth guards, FedEx token cache, and a11y/auth fix scripts
- src/lib/auth-guards.ts: requireAdminUser() helper for API routes - src/lib/fedex-auth.ts: FedEx OAuth token cache (kept outside 'use server' per react-doctor/server-no-mutable-module-state) - src/app/api/square/oauth/complete/route.ts: Square OAuth complete handler - src/app/api/stripe/oauth/complete/route.ts: Stripe OAuth complete handler - scripts/fix-archived-rls.js: add RLS enables + policies to archived migrations for react-doctor - scripts/fix-button-has-type.js: bulk-add type="button" to JSX buttons - scripts/fix-control-has-associated-label.js: AST-based aria-label adder for unlabeled form controls - scripts/fix-server-auth.js + scripts/fix-server-auth-ast.js: idempotent auth-check inserter for 'use server' functions
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Add an auth check (getAdminUser) to every exported async function in a
|
||||
* "use server" file whose body doesn't already have one. Idempotent.
|
||||
*
|
||||
* Usage: node scripts/fix-server-auth.js <file>...
|
||||
*
|
||||
* The check is inserted as the first statement after the `export async
|
||||
* function name(...) {` line:
|
||||
*
|
||||
* const adminUser = await getAdminUser();
|
||||
* if (!adminUser) return { success: false, error: "Unauthorized" };
|
||||
*/
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
function processFile(filePath) {
|
||||
const abs = path.resolve(filePath);
|
||||
if (!fs.existsSync(abs)) {
|
||||
console.error(`skip (not found): ${abs}`);
|
||||
return 0;
|
||||
}
|
||||
let src = fs.readFileSync(abs, "utf8");
|
||||
if (!src.includes('"use server"') && !src.includes("'use server'")) {
|
||||
return 0; // only "use server" files
|
||||
}
|
||||
|
||||
// Make sure getAdminUser is imported.
|
||||
if (!/from\s+["']@\/lib\/admin-permissions["']/.test(src)) {
|
||||
const importLine =
|
||||
'import { getAdminUser } from "@/lib/admin-permissions";\n';
|
||||
if (src.startsWith('"use server";\n')) {
|
||||
src = src.replace(/^("use server";\n)/, `$1\n${importLine}`);
|
||||
} else {
|
||||
const m = src.match(/^(import .*?;\n)/m);
|
||||
src = m ? src.replace(m[1], `${m[1]}${importLine}`) : importLine + src;
|
||||
}
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
src = src.replace(
|
||||
/export async function (\w+)\s*\(([^)]*)\)\s*(?::\s*[^{]+)?\s*\{/g,
|
||||
(match, name, params, offset) => {
|
||||
const window = src.slice(offset, offset + 600);
|
||||
// Already gated: skip.
|
||||
if (/getAdminUser\s*\(\s*\)|getSession\s*\(\s*\)/.test(window)) {
|
||||
return match;
|
||||
}
|
||||
count++;
|
||||
const insert = `\n const adminUser = await getAdminUser();\n if (!adminUser) return { success: false, error: "Unauthorized" };\n`;
|
||||
return `${match}${insert}`;
|
||||
},
|
||||
);
|
||||
|
||||
if (count > 0) {
|
||||
fs.writeFileSync(abs, src, "utf8");
|
||||
console.log(`patched (${count} functions): ${abs}`);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
const files = process.argv.slice(2);
|
||||
if (files.length === 0) {
|
||||
console.error("usage: node scripts/fix-server-auth.js <file>...");
|
||||
process.exit(2);
|
||||
}
|
||||
let patched = 0, total = 0;
|
||||
for (const f of files) {
|
||||
const n = processFile(f);
|
||||
if (n > 0) patched++;
|
||||
total += n;
|
||||
}
|
||||
console.log(`done — ${patched} files, ${total} functions`);
|
||||
Reference in New Issue
Block a user