deckhearth/pages/api/auth-utils.js

58 lines
1.3 KiB
JavaScript
Raw Permalink Normal View History

import jwt from 'jsonwebtoken';
refactor(db): collapse @neondatabase/serverless onto @vercel/postgres + delete lib/database.js Convoy: single-sql-client (P1 quality, launch sequence step 8) Addresses: AGENTS.md Gotcha #1, .convoys/ship-readiness.md P1 #8 ## Decisions - D1: Caller inventory = 2 files (1 source + 1 test), not "~3 based on graph". Only pages/api/auth-utils.js imports `db`; test/api/auth-utils.test.js mocks it purely to satisfy the import graph (the 5 tests exercise generateToken/verifyToken, not isAdmin/getUserById). - D2: Migrate both call sites (isAdmin, getUserById) to @vercel/postgres tagged-template SQL. Queries are SELECT-only, single-table, single-numeric-parameter — byte-equivalent translation; same result shape ({rows, rowCount}); no transaction or pool semantics differ. - D3: KEEP @neondatabase/serverless as a dep. 11 scripts/* files still use `neon()` directly (setup-neon-db.js, migrations/, reset-db.js, 8 historical add-*/fix-*/seed-* jobs). They are out of scope per the no-go-zones rule and the convoy spec; purging the dep entirely would be its own convoy (queued as `purge-neondatabase-serverless-fully`, blocked on migration-tool). - D4: sql.unsafe audit — NOT a real injection vector with current callers (userId comes from a verified JWT, is a numeric SERIAL id). Security finding: NO. Pure refactor + foot-gun removal that prevents the FUTURE caller that would have been the incident. - D5: Test mock cleanup — drop the now-unneeded `vi.mock('../../lib/database.js')` call + unused `vi` import. Test count + assertions unchanged (5/5). ## Per-file changes - pages/api/auth-utils.js: swap `import { db } from '../../lib/database.js'` for `import { sql } from '@vercel/postgres'`; rewrite isAdmin's `db.query(SELECT … WHERE id = $1, [userId])` and getUserById's same shape to `sql\`SELECT … WHERE id = ${userId}\``. Same try/catch, same result.rows[0] access, same error returns. - test/api/auth-utils.test.js: drop vi.mock for lib/database.js + the unused `vi` import. 5/5 tests still pass. - lib/database.js: DELETED (47 lines removed; manual-interpolation + sql.unsafe wrapper is gone). - .convoys/single-sql-client.md: NEW (the convoy file documenting all decisions + caller inventory + verification + risks + follow-ups). ## Verification - npm run lint → 128 problems (baseline preserved, no regression) - npm run test:run → 21/21 pass (vitest) - Grep "lib/database" --type js -l → 0 hits anywhere - Grep "@neondatabase/serverless" --type js -l → still matches the 11 scripts/* sites (expected; out of scope per D3) - node --check pages/api/auth-utils.js → exit 0 ## Scope note This convoy collapses the lib/database.js abstraction onto the canonical @vercel/postgres surface for pages/api/**. It does NOT eliminate @neondatabase/serverless from the dependency tree — that would require migrating the scripts/* helpers, which is out of scope here (no-go-zones rule + convoy spec). Queued as a follow-up. ## Live smoke Deferred. The two migrated functions (isAdmin, getUserById) are only reachable via pages/api/admin/index.js which requires an admin Bearer token and a populated users table in prod Neon. Byte-equivalent SQL + identical result shape gives high confidence; rollback is a single-commit revert if a post-merge admin action 500s. Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 23:50:58 -04:00
import { sql } from '@vercel/postgres';
import { JWT_SECRET, JWT_TOKEN_TTL } from '../../lib/auth-secret.js';
export async function hashPassword(password) {
const bcrypt = await import('bcryptjs');
return await bcrypt.hash(password, 12);
}
export async function verifyPassword(password, hashedPassword) {
const bcrypt = await import('bcryptjs');
return await bcrypt.compare(password, hashedPassword);
}
export function generateToken(user) {
return jwt.sign(
refactor(db): collapse @neondatabase/serverless onto @vercel/postgres + delete lib/database.js Convoy: single-sql-client (P1 quality, launch sequence step 8) Addresses: AGENTS.md Gotcha #1, .convoys/ship-readiness.md P1 #8 ## Decisions - D1: Caller inventory = 2 files (1 source + 1 test), not "~3 based on graph". Only pages/api/auth-utils.js imports `db`; test/api/auth-utils.test.js mocks it purely to satisfy the import graph (the 5 tests exercise generateToken/verifyToken, not isAdmin/getUserById). - D2: Migrate both call sites (isAdmin, getUserById) to @vercel/postgres tagged-template SQL. Queries are SELECT-only, single-table, single-numeric-parameter — byte-equivalent translation; same result shape ({rows, rowCount}); no transaction or pool semantics differ. - D3: KEEP @neondatabase/serverless as a dep. 11 scripts/* files still use `neon()` directly (setup-neon-db.js, migrations/, reset-db.js, 8 historical add-*/fix-*/seed-* jobs). They are out of scope per the no-go-zones rule and the convoy spec; purging the dep entirely would be its own convoy (queued as `purge-neondatabase-serverless-fully`, blocked on migration-tool). - D4: sql.unsafe audit — NOT a real injection vector with current callers (userId comes from a verified JWT, is a numeric SERIAL id). Security finding: NO. Pure refactor + foot-gun removal that prevents the FUTURE caller that would have been the incident. - D5: Test mock cleanup — drop the now-unneeded `vi.mock('../../lib/database.js')` call + unused `vi` import. Test count + assertions unchanged (5/5). ## Per-file changes - pages/api/auth-utils.js: swap `import { db } from '../../lib/database.js'` for `import { sql } from '@vercel/postgres'`; rewrite isAdmin's `db.query(SELECT … WHERE id = $1, [userId])` and getUserById's same shape to `sql\`SELECT … WHERE id = ${userId}\``. Same try/catch, same result.rows[0] access, same error returns. - test/api/auth-utils.test.js: drop vi.mock for lib/database.js + the unused `vi` import. 5/5 tests still pass. - lib/database.js: DELETED (47 lines removed; manual-interpolation + sql.unsafe wrapper is gone). - .convoys/single-sql-client.md: NEW (the convoy file documenting all decisions + caller inventory + verification + risks + follow-ups). ## Verification - npm run lint → 128 problems (baseline preserved, no regression) - npm run test:run → 21/21 pass (vitest) - Grep "lib/database" --type js -l → 0 hits anywhere - Grep "@neondatabase/serverless" --type js -l → still matches the 11 scripts/* sites (expected; out of scope per D3) - node --check pages/api/auth-utils.js → exit 0 ## Scope note This convoy collapses the lib/database.js abstraction onto the canonical @vercel/postgres surface for pages/api/**. It does NOT eliminate @neondatabase/serverless from the dependency tree — that would require migrating the scripts/* helpers, which is out of scope here (no-go-zones rule + convoy spec). Queued as a follow-up. ## Live smoke Deferred. The two migrated functions (isAdmin, getUserById) are only reachable via pages/api/admin/index.js which requires an admin Bearer token and a populated users table in prod Neon. Byte-equivalent SQL + identical result shape gives high confidence; rollback is a single-commit revert if a post-merge admin action 500s. Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 23:50:58 -04:00
{
userId: user.id,
email: user.email,
role: user.role
},
JWT_SECRET,
{ expiresIn: JWT_TOKEN_TTL }
);
}
export function verifyToken(token) {
try {
return jwt.verify(token, JWT_SECRET);
} catch (error) {
return null;
}
}
export async function isAdmin(userId) {
try {
refactor(db): collapse @neondatabase/serverless onto @vercel/postgres + delete lib/database.js Convoy: single-sql-client (P1 quality, launch sequence step 8) Addresses: AGENTS.md Gotcha #1, .convoys/ship-readiness.md P1 #8 ## Decisions - D1: Caller inventory = 2 files (1 source + 1 test), not "~3 based on graph". Only pages/api/auth-utils.js imports `db`; test/api/auth-utils.test.js mocks it purely to satisfy the import graph (the 5 tests exercise generateToken/verifyToken, not isAdmin/getUserById). - D2: Migrate both call sites (isAdmin, getUserById) to @vercel/postgres tagged-template SQL. Queries are SELECT-only, single-table, single-numeric-parameter — byte-equivalent translation; same result shape ({rows, rowCount}); no transaction or pool semantics differ. - D3: KEEP @neondatabase/serverless as a dep. 11 scripts/* files still use `neon()` directly (setup-neon-db.js, migrations/, reset-db.js, 8 historical add-*/fix-*/seed-* jobs). They are out of scope per the no-go-zones rule and the convoy spec; purging the dep entirely would be its own convoy (queued as `purge-neondatabase-serverless-fully`, blocked on migration-tool). - D4: sql.unsafe audit — NOT a real injection vector with current callers (userId comes from a verified JWT, is a numeric SERIAL id). Security finding: NO. Pure refactor + foot-gun removal that prevents the FUTURE caller that would have been the incident. - D5: Test mock cleanup — drop the now-unneeded `vi.mock('../../lib/database.js')` call + unused `vi` import. Test count + assertions unchanged (5/5). ## Per-file changes - pages/api/auth-utils.js: swap `import { db } from '../../lib/database.js'` for `import { sql } from '@vercel/postgres'`; rewrite isAdmin's `db.query(SELECT … WHERE id = $1, [userId])` and getUserById's same shape to `sql\`SELECT … WHERE id = ${userId}\``. Same try/catch, same result.rows[0] access, same error returns. - test/api/auth-utils.test.js: drop vi.mock for lib/database.js + the unused `vi` import. 5/5 tests still pass. - lib/database.js: DELETED (47 lines removed; manual-interpolation + sql.unsafe wrapper is gone). - .convoys/single-sql-client.md: NEW (the convoy file documenting all decisions + caller inventory + verification + risks + follow-ups). ## Verification - npm run lint → 128 problems (baseline preserved, no regression) - npm run test:run → 21/21 pass (vitest) - Grep "lib/database" --type js -l → 0 hits anywhere - Grep "@neondatabase/serverless" --type js -l → still matches the 11 scripts/* sites (expected; out of scope per D3) - node --check pages/api/auth-utils.js → exit 0 ## Scope note This convoy collapses the lib/database.js abstraction onto the canonical @vercel/postgres surface for pages/api/**. It does NOT eliminate @neondatabase/serverless from the dependency tree — that would require migrating the scripts/* helpers, which is out of scope here (no-go-zones rule + convoy spec). Queued as a follow-up. ## Live smoke Deferred. The two migrated functions (isAdmin, getUserById) are only reachable via pages/api/admin/index.js which requires an admin Bearer token and a populated users table in prod Neon. Byte-equivalent SQL + identical result shape gives high confidence; rollback is a single-commit revert if a post-merge admin action 500s. Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 23:50:58 -04:00
const result = await sql`
SELECT role FROM users WHERE id = ${userId}
`;
return result.rows[0]?.role === 'admin';
} catch (error) {
console.error('Error checking admin status:', error);
return false;
}
}
export async function getUserById(userId) {
try {
refactor(db): collapse @neondatabase/serverless onto @vercel/postgres + delete lib/database.js Convoy: single-sql-client (P1 quality, launch sequence step 8) Addresses: AGENTS.md Gotcha #1, .convoys/ship-readiness.md P1 #8 ## Decisions - D1: Caller inventory = 2 files (1 source + 1 test), not "~3 based on graph". Only pages/api/auth-utils.js imports `db`; test/api/auth-utils.test.js mocks it purely to satisfy the import graph (the 5 tests exercise generateToken/verifyToken, not isAdmin/getUserById). - D2: Migrate both call sites (isAdmin, getUserById) to @vercel/postgres tagged-template SQL. Queries are SELECT-only, single-table, single-numeric-parameter — byte-equivalent translation; same result shape ({rows, rowCount}); no transaction or pool semantics differ. - D3: KEEP @neondatabase/serverless as a dep. 11 scripts/* files still use `neon()` directly (setup-neon-db.js, migrations/, reset-db.js, 8 historical add-*/fix-*/seed-* jobs). They are out of scope per the no-go-zones rule and the convoy spec; purging the dep entirely would be its own convoy (queued as `purge-neondatabase-serverless-fully`, blocked on migration-tool). - D4: sql.unsafe audit — NOT a real injection vector with current callers (userId comes from a verified JWT, is a numeric SERIAL id). Security finding: NO. Pure refactor + foot-gun removal that prevents the FUTURE caller that would have been the incident. - D5: Test mock cleanup — drop the now-unneeded `vi.mock('../../lib/database.js')` call + unused `vi` import. Test count + assertions unchanged (5/5). ## Per-file changes - pages/api/auth-utils.js: swap `import { db } from '../../lib/database.js'` for `import { sql } from '@vercel/postgres'`; rewrite isAdmin's `db.query(SELECT … WHERE id = $1, [userId])` and getUserById's same shape to `sql\`SELECT … WHERE id = ${userId}\``. Same try/catch, same result.rows[0] access, same error returns. - test/api/auth-utils.test.js: drop vi.mock for lib/database.js + the unused `vi` import. 5/5 tests still pass. - lib/database.js: DELETED (47 lines removed; manual-interpolation + sql.unsafe wrapper is gone). - .convoys/single-sql-client.md: NEW (the convoy file documenting all decisions + caller inventory + verification + risks + follow-ups). ## Verification - npm run lint → 128 problems (baseline preserved, no regression) - npm run test:run → 21/21 pass (vitest) - Grep "lib/database" --type js -l → 0 hits anywhere - Grep "@neondatabase/serverless" --type js -l → still matches the 11 scripts/* sites (expected; out of scope per D3) - node --check pages/api/auth-utils.js → exit 0 ## Scope note This convoy collapses the lib/database.js abstraction onto the canonical @vercel/postgres surface for pages/api/**. It does NOT eliminate @neondatabase/serverless from the dependency tree — that would require migrating the scripts/* helpers, which is out of scope here (no-go-zones rule + convoy spec). Queued as a follow-up. ## Live smoke Deferred. The two migrated functions (isAdmin, getUserById) are only reachable via pages/api/admin/index.js which requires an admin Bearer token and a populated users table in prod Neon. Byte-equivalent SQL + identical result shape gives high confidence; rollback is a single-commit revert if a post-merge admin action 500s. Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 23:50:58 -04:00
const result = await sql`
SELECT id, email, role, created_at FROM users WHERE id = ${userId}
`;
return result.rows[0];
} catch (error) {
console.error('Error getting user:', error);
return null;
}
refactor(db): collapse @neondatabase/serverless onto @vercel/postgres + delete lib/database.js Convoy: single-sql-client (P1 quality, launch sequence step 8) Addresses: AGENTS.md Gotcha #1, .convoys/ship-readiness.md P1 #8 ## Decisions - D1: Caller inventory = 2 files (1 source + 1 test), not "~3 based on graph". Only pages/api/auth-utils.js imports `db`; test/api/auth-utils.test.js mocks it purely to satisfy the import graph (the 5 tests exercise generateToken/verifyToken, not isAdmin/getUserById). - D2: Migrate both call sites (isAdmin, getUserById) to @vercel/postgres tagged-template SQL. Queries are SELECT-only, single-table, single-numeric-parameter — byte-equivalent translation; same result shape ({rows, rowCount}); no transaction or pool semantics differ. - D3: KEEP @neondatabase/serverless as a dep. 11 scripts/* files still use `neon()` directly (setup-neon-db.js, migrations/, reset-db.js, 8 historical add-*/fix-*/seed-* jobs). They are out of scope per the no-go-zones rule and the convoy spec; purging the dep entirely would be its own convoy (queued as `purge-neondatabase-serverless-fully`, blocked on migration-tool). - D4: sql.unsafe audit — NOT a real injection vector with current callers (userId comes from a verified JWT, is a numeric SERIAL id). Security finding: NO. Pure refactor + foot-gun removal that prevents the FUTURE caller that would have been the incident. - D5: Test mock cleanup — drop the now-unneeded `vi.mock('../../lib/database.js')` call + unused `vi` import. Test count + assertions unchanged (5/5). ## Per-file changes - pages/api/auth-utils.js: swap `import { db } from '../../lib/database.js'` for `import { sql } from '@vercel/postgres'`; rewrite isAdmin's `db.query(SELECT … WHERE id = $1, [userId])` and getUserById's same shape to `sql\`SELECT … WHERE id = ${userId}\``. Same try/catch, same result.rows[0] access, same error returns. - test/api/auth-utils.test.js: drop vi.mock for lib/database.js + the unused `vi` import. 5/5 tests still pass. - lib/database.js: DELETED (47 lines removed; manual-interpolation + sql.unsafe wrapper is gone). - .convoys/single-sql-client.md: NEW (the convoy file documenting all decisions + caller inventory + verification + risks + follow-ups). ## Verification - npm run lint → 128 problems (baseline preserved, no regression) - npm run test:run → 21/21 pass (vitest) - Grep "lib/database" --type js -l → 0 hits anywhere - Grep "@neondatabase/serverless" --type js -l → still matches the 11 scripts/* sites (expected; out of scope per D3) - node --check pages/api/auth-utils.js → exit 0 ## Scope note This convoy collapses the lib/database.js abstraction onto the canonical @vercel/postgres surface for pages/api/**. It does NOT eliminate @neondatabase/serverless from the dependency tree — that would require migrating the scripts/* helpers, which is out of scope here (no-go-zones rule + convoy spec). Queued as a follow-up. ## Live smoke Deferred. The two migrated functions (isAdmin, getUserById) are only reachable via pages/api/admin/index.js which requires an admin Bearer token and a populated users table in prod Neon. Byte-equivalent SQL + identical result shape gives high confidence; rollback is a single-commit revert if a post-merge admin action 500s. Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 23:50:58 -04:00
}