Function bodies 115 total
main function · typescript · L10-L152 (143 LOC)scripts/migrate-to-database.ts
async function main() {
console.log('Starting database migration...\n')
// 1. Migrate Employees
console.log('📋 Migrating employees...')
const employeesPath = path.join(process.cwd(), 'data', 'employees.json')
const employeesData = JSON.parse(fs.readFileSync(employeesPath, 'utf-8'))
let employeeCount = 0
for (const emp of employeesData) {
await prisma.employee.upsert({
where: { id: emp.id },
update: {
firstName: emp.firstName,
lastName: emp.lastName,
email: emp.email || null,
extension: emp.extension || null,
phoneNumber: emp.phoneNumber || null,
did: emp.did || null,
location: emp.location || 'Unknown',
team: emp.team || emp.department || 'General',
title: emp.title || null,
jobTitle: emp.jobTitle || null,
department: emp.department || null,
photoUrl: emp.photoUrl || null,
avatarUrl: emp.avatarUrl || null,
},
create: {
id: emp.id,
log function · typescript · L43-L46 (4 LOC)scripts/nextiva-scraper.ts
function log(message: string): void {
const timestamp = new Date().toISOString()
console.log(`[${timestamp}] ${message}`)
}logError function · typescript · L48-L51 (4 LOC)scripts/nextiva-scraper.ts
function logError(message: string): void {
const timestamp = new Date().toISOString()
console.error(`[${timestamp}] ERROR: ${message}`)
}validateEnv function · typescript · L53-L73 (21 LOC)scripts/nextiva-scraper.ts
function validateEnv(): Record<(typeof REQUIRED_ENV_VARS)[number], string> {
const missing: string[] = []
const env: Record<string, string> = {}
for (const key of REQUIRED_ENV_VARS) {
const value = process.env[key]
if (!value || value.trim().length === 0) {
missing.push(key)
} else {
env[key] = value.trim()
}
}
if (missing.length > 0) {
throw new Error(
`Missing required environment variables: ${missing.join(', ')}`
)
}
return env as Record<(typeof REQUIRED_ENV_VARS)[number], string>
}ensureScreenshotDir function · typescript · L75-L79 (5 LOC)scripts/nextiva-scraper.ts
function ensureScreenshotDir(): void {
if (!existsSync(SCREENSHOT_DIR)) {
mkdirSync(SCREENSHOT_DIR, { recursive: true })
}
}takeScreenshot function · typescript · L81-L95 (15 LOC)scripts/nextiva-scraper.ts
async function takeScreenshot(
page: Page,
name: string
): Promise<string | null> {
try {
ensureScreenshotDir()
const filepath = join(SCREENSHOT_DIR, `${name}.png`)
await page.screenshot({ path: filepath, fullPage: true })
log(`Screenshot saved: ${filepath}`)
return filepath
} catch (err) {
logError(`Failed to take screenshot "${name}": ${err}`)
return null
}
}login function · typescript · L101-L203 (103 LOC)scripts/nextiva-scraper.ts
async function login(
page: Page,
username: string,
password: string
): Promise<void> {
let lastError: Error | null = null
for (let attempt = 1; attempt <= MAX_LOGIN_ATTEMPTS; attempt++) {
log(`Login attempt ${attempt}/${MAX_LOGIN_ATTEMPTS}...`)
try {
await page.goto(LOGIN_URL, {
waitUntil: 'networkidle',
timeout: NAVIGATION_TIMEOUT_MS,
})
log('Login page loaded, filling credentials...')
// Nextiva login form — wait for the email/username input
// The login form may use different selectors; try common patterns
const emailSelector = await page
.waitForSelector(
'input[type="email"], input[name="username"], input[name="email"], input#email, input#username',
{ timeout: LOGIN_TIMEOUT_MS }
)
if (!emailSelector) {
throw new Error('Could not find email/username input field')
}
await emailSelector.fill(username)
log('Username entered')
// Some lCitation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
navigateToUsersPage function · typescript · L209-L276 (68 LOC)scripts/nextiva-scraper.ts
async function navigateToUsersPage(page: Page): Promise<void> {
log('Navigating to Users management page...')
// Try direct URL navigation first (most Nextiva admin portals use this path)
const usersUrls = [
'https://admin.nextiva.com/users',
'https://admin.nextiva.com/#/users',
'https://admin.nextiva.com/management/users',
]
for (const url of usersUrls) {
try {
await page.goto(url, {
waitUntil: 'networkidle',
timeout: NAVIGATION_TIMEOUT_MS,
})
// Check if we landed on a page with user-related content
const hasUsersContent = await page
.waitForSelector(
'text=Users, text=User Management, [data-testid="users"], table',
{ timeout: 10_000 }
)
.catch(() => null)
if (hasUsersContent) {
log(`Users page loaded via direct URL: ${url}`)
return
}
} catch {
// Try next URL
}
}
// Fallback: navigate via the sidebar/menu
log('Direct URL nexportUsersCSV function · typescript · L278-L358 (81 LOC)scripts/nextiva-scraper.ts
async function exportUsersCSV(page: Page): Promise<string> {
log('Looking for CSV export button...')
// Common export button selectors for Nextiva admin
const exportSelectors = [
'button:has-text("Export")',
'button:has-text("Download")',
'button:has-text("Export CSV")',
'button:has-text("Download CSV")',
'a:has-text("Export")',
'a:has-text("Download")',
'[data-testid="export"]',
'[data-testid="export-csv"]',
'[aria-label="Export"]',
'[aria-label="Download"]',
'button >> svg', // Icon-only export buttons
]
let exportButton = null
for (const selector of exportSelectors) {
try {
exportButton = await page.waitForSelector(selector, {
timeout: 5_000,
})
if (exportButton) {
log(`Found export button with selector: ${selector}`)
break
}
} catch {
// Try next selector
}
}
if (!exportButton) {
await takeScreenshot(page, 'export-button-not-found')
throw new ErruploadCSV function · typescript · L364-L398 (35 LOC)scripts/nextiva-scraper.ts
async function uploadCSV(
csvContent: string,
uploadUrl: string,
syncSecret: string
): Promise<void> {
log(`Uploading CSV to ${uploadUrl}...`)
const response = await fetch(uploadUrl, {
method: 'POST',
headers: {
'Authorization': `Bearer ${syncSecret}`,
'Content-Type': 'text/csv',
},
body: csvContent,
})
const responseText = await response.text()
if (!response.ok) {
logError(`Sync endpoint returned ${response.status}: ${responseText}`)
throw new Error(
`Sync upload failed with status ${response.status}: ${responseText}`
)
}
// Try to parse as JSON for structured logging
try {
const result = JSON.parse(responseText)
log(`Sync result: ${JSON.stringify(result, null, 2)}`)
} catch {
log(`Sync response: ${responseText}`)
}
log('CSV uploaded successfully')
}main function · typescript · L404-L462 (59 LOC)scripts/nextiva-scraper.ts
async function main(): Promise<void> {
log('=== Nextiva CSV Sync — Starting ===')
// Validate environment
const env = validateEnv()
log('Environment variables validated')
let browser: Browser | null = null
try {
// Launch browser
log('Launching Chromium (headless)...')
browser = await chromium.launch({
headless: true,
})
const context = await browser.newContext({
acceptDownloads: true,
viewport: { width: 1280, height: 720 },
userAgent:
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
})
const page = await context.newPage()
// Set default timeouts
page.setDefaultTimeout(NAVIGATION_TIMEOUT_MS)
page.setDefaultNavigationTimeout(NAVIGATION_TIMEOUT_MS)
// Step 1: Login
await login(page, env.NEXTIVA_USERNAME, env.NEXTIVA_PASSWORD)
// Step 2: Navigate to Users page
await navigateToUsersPage(page)
// Step 3: Export CSVmain function · typescript · L16-L29 (14 LOC)scripts/seed-allowed-ips.ts
async function main() {
console.log('Seeding allowed IPs...')
for (const entry of OFFICE_IPS) {
await prisma.allowedIP.upsert({
where: { ip: entry.ip },
update: { location: entry.location, notes: entry.notes },
create: { ...entry, addedBy: 'system-seed' },
})
console.log(` ✓ ${entry.ip} (${entry.location})`)
}
console.log(`\nSeeded ${OFFICE_IPS.length} IPs.`)
}getErrorMessage function · typescript · L9-L25 (17 LOC)src/app/admin/login/page.tsx
function getErrorMessage(code: string | null): string {
switch (code) {
case 'AccessDenied':
return 'Access denied. Your Microsoft account is not registered as an admin.'
case 'NoSession':
return 'Microsoft sign-in session expired. Please try again.'
case 'NotRegistered':
return 'Your account is not registered as an admin. Contact your administrator.'
case 'CallbackError':
return 'Something went wrong during sign-in. Please try again.'
case 'OAuthSignin':
case 'OAuthCallback':
return 'Microsoft sign-in failed. Please try again.'
default:
return code ? 'Sign-in failed. Please try again.' : ''
}
}AdminLoginPage function · typescript · L31-L47 (17 LOC)src/app/admin/login/page.tsx
export default function AdminLoginPage() {
return (
<Suspense fallback={
<div style={{
minHeight: '100vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: 'linear-gradient(135deg, #0a1f38 0%, #0f2b4c 50%, #163d6b 100%)',
}}>
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '14px' }}>Loading...</div>
</div>
}>
<LoginContent />
</Suspense>
)
}LoginContent function · typescript · L49-L372 (324 LOC)src/app/admin/login/page.tsx
function LoginContent() {
const router = useRouter()
const searchParams = useSearchParams()
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [error, setError] = useState(() => getErrorMessage(searchParams.get('error')))
const [loading, setLoading] = useState(false)
const [msLoading, setMsLoading] = useState(false)
const handleMicrosoftSignIn = async () => {
setError('')
setMsLoading(true)
try {
await signIn('azure-ad', {
callbackUrl: '/api/admin/microsoft-callback',
})
} catch {
setError('Failed to initiate Microsoft sign-in.')
setMsLoading(false)
}
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
setLoading(true)
try {
const response = await fetch('/api/admin/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
})
Repobility — the code-quality scanner for AI-generated software · https://repobility.com
AdminPage function · typescript · L3-L5 (3 LOC)src/app/admin/page.tsx
export default function AdminPage() {
return <AdminDashboard />
}GET function · typescript · L5-L18 (14 LOC)src/app/api/admin/activity/route.ts
export async function GET() {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
// Activity log not yet migrated to database — return empty for now
return NextResponse.json([])
} catch (error) {
console.error('Error reading activity log:', error)
return NextResponse.json({ error: 'Failed to fetch activity log' }, { status: 500 })
}
}GET function · typescript · L6-L32 (27 LOC)src/app/api/admin/ips/route.ts
export async function GET() {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const allowedIPs = await prisma.allowedIP.findMany({
orderBy: { addedAt: 'desc' }
})
const mapped = allowedIPs.map((record) => ({
id: record.id,
ip: record.ip,
location: record.location,
notes: record.notes,
addedBy: record.addedBy,
addedAt: record.addedAt.toISOString()
}))
return NextResponse.json(mapped)
} catch (error) {
console.error('Error fetching allowed IPs:', error)
return NextResponse.json({ error: 'Failed to fetch allowed IPs' }, { status: 500 })
}
}POST function · typescript · L35-L100 (66 LOC)src/app/api/admin/ips/route.ts
export async function POST(request: NextRequest) {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
if (!isSuperAdmin(user)) {
return NextResponse.json({ error: 'Forbidden - Superadmin only' }, { status: 403 })
}
const { ip, location, notes } = await request.json()
if (!ip || !location) {
return NextResponse.json({ error: 'Missing required fields (ip, location)' }, { status: 400 })
}
// Validate IPv4 format
const parts = ip.split('.')
if (parts.length !== 4) {
return NextResponse.json({ error: 'Invalid IPv4 format' }, { status: 400 })
}
const isValidIPv4 = parts.every((part: string) => {
const num = parseInt(part, 10)
return !isNaN(num) && num >= 0 && num <= 255 && String(num) === part
})
if (!isValidIPv4) {
return NextResponse.json({ error: 'Invalid IPv4 format' }, { status: 400 })
}
// DELETE function · typescript · L103-L139 (37 LOC)src/app/api/admin/ips/route.ts
export async function DELETE(request: NextRequest) {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
if (!isSuperAdmin(user)) {
return NextResponse.json({ error: 'Forbidden - Superadmin only' }, { status: 403 })
}
const { searchParams } = new URL(request.url)
const id = searchParams.get('id')
if (!id) {
return NextResponse.json({ error: 'IP id is required as query parameter' }, { status: 400 })
}
const existing = await prisma.allowedIP.findUnique({
where: { id }
})
if (!existing) {
return NextResponse.json({ error: 'IP record not found' }, { status: 404 })
}
await prisma.allowedIP.delete({
where: { id }
})
return NextResponse.json({ success: true })
} catch (error) {
console.error('Error deleting allowed IP:', error)
return NextResponse.json({ error: 'Failed to delete allowed IP' }, { POST function · typescript · L6-L75 (70 LOC)src/app/api/admin/login/route.ts
export async function POST(request: NextRequest) {
try {
const { email, password } = await request.json()
if (!email || !password) {
return NextResponse.json(
{ error: 'Email and password are required' },
{ status: 400 }
)
}
const user = await prisma.user.findFirst({
where: {
email: {
equals: email,
mode: 'insensitive'
}
}
})
if (!user) {
return NextResponse.json(
{ error: 'Invalid email or password' },
{ status: 401 }
)
}
// SSO-only users don't have a password -- they must use Microsoft sign-in
if (!user.passwordHash) {
return NextResponse.json(
{ error: 'This account uses Microsoft sign-in. Please use the "Sign in with Microsoft" button.' },
{ status: 401 }
)
}
// Verify password
const isValid = await verifyPassword(password, user.passwordHash)
if (!isValid) {
return NextResponse.json(
POST function · typescript · L4-L16 (13 LOC)src/app/api/admin/logout/route.ts
export async function POST() {
try {
await clearSessionCookie()
return NextResponse.json({ success: true })
} catch (error) {
console.error('Logout error:', error)
return NextResponse.json(
{ error: 'Logout failed' },
{ status: 500 }
)
}
}GET function · typescript · L13-L65 (53 LOC)src/app/api/admin/microsoft-callback/route.ts
export async function GET(request: NextRequest) {
try {
// Get the next-auth session (contains Microsoft profile data)
const session = await auth()
if (!session?.user?.email) {
const loginUrl = new URL('/admin/login', request.url)
loginUrl.searchParams.set('error', 'NoSession')
return NextResponse.redirect(loginUrl)
}
// Look up the user in our User table by email (case-insensitive)
const dbUser = await prisma.user.findFirst({
where: {
email: {
equals: session.user.email,
mode: 'insensitive',
},
},
})
if (!dbUser) {
const loginUrl = new URL('/admin/login', request.url)
loginUrl.searchParams.set('error', 'NotRegistered')
return NextResponse.redirect(loginUrl)
}
// Update microsoftId if not already set
const microsoftId = session.microsoftId
if (microsoftId && !dbUser.microsoftId) {
await prisma.user.update({
where: { id: dbUser.id },
Repobility · code-quality intelligence platform · https://repobility.com
GET function · typescript · L5-L138 (134 LOC)src/app/api/admin/notify-approved/route.ts
export async function GET(request: NextRequest) {
try {
// Verify cron secret for Vercel cron jobs, or reject unauthenticated calls
const authHeader = request.headers.get('authorization')
const cronSecret = process.env.CRON_SECRET
if (cronSecret && authHeader !== `Bearer ${cronSecret}`) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const resend = new Resend(process.env.RESEND_API_KEY)
// Read approved changes from database
const approvedChanges = await prisma.pendingChange.findMany({
where: { status: 'approved' }
})
if (approvedChanges.length === 0) {
return NextResponse.json({
success: true,
message: 'No approved changes to notify about',
sent: false
})
}
// Count changes by type
const changesByType = {
add: approvedChanges.filter(c => c.type === 'add').length,
edit: approvedChanges.filter(c => c.type === 'edit').length,
delete: approvedmapToPendingChange function · typescript · L8-L34 (27 LOC)src/app/api/admin/pending/route.ts
function mapToPendingChange(dbChange: {
id: string
type: string
employeeId: string | null
beforeData: unknown
afterData: unknown
status: string
proposedBy: string
proposedAt: Date
approvedBy: string | null
approvedAt: Date | null
notes: string | null
}): PendingChange {
return {
id: dbChange.id,
type: dbChange.type as 'add' | 'edit' | 'delete',
employeeId: dbChange.employeeId || undefined,
before: dbChange.beforeData as Employee | undefined,
after: dbChange.afterData as Employee | undefined,
status: dbChange.status as 'pending' | 'approved' | 'rejected',
proposedBy: dbChange.proposedBy,
proposedAt: dbChange.proposedAt.toISOString(),
approvedBy: dbChange.approvedBy || undefined,
approvedAt: dbChange.approvedAt?.toISOString(),
notes: dbChange.notes || undefined,
}
}GET function · typescript · L37-L52 (16 LOC)src/app/api/admin/pending/route.ts
export async function GET() {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const changes = await prisma.pendingChange.findMany({
orderBy: { proposedAt: 'desc' }
})
return NextResponse.json(changes.map(mapToPendingChange))
} catch (error) {
console.error('Error fetching pending changes:', error)
return NextResponse.json({ error: 'Failed to fetch pending changes' }, { status: 500 })
}
}POST function · typescript · L55-L85 (31 LOC)src/app/api/admin/pending/route.ts
export async function POST(request: NextRequest) {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const change: PendingChange = await request.json()
const newChange = await prisma.pendingChange.create({
data: {
id: `change-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
type: change.type,
employeeId: change.employeeId || null,
beforeData: change.before ? (change.before as object) : undefined,
afterData: change.after ? (change.after as object) : undefined,
status: change.status || 'pending',
proposedBy: change.proposedBy,
proposedAt: change.proposedAt ? new Date(change.proposedAt) : new Date(),
approvedBy: change.approvedBy || null,
approvedAt: change.approvedAt ? new Date(change.approvedAt) : null,
notes: change.notes || null,
}
})
return NextResponse.jsoPATCH function · typescript · L88-L119 (32 LOC)src/app/api/admin/pending/route.ts
export async function PATCH(request: NextRequest) {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
if (!canApprove(user)) {
return NextResponse.json({ error: 'Forbidden - Approver or Super Admin only' }, { status: 403 })
}
const { id, status, notes, approvedBy, approvedAt } = await request.json()
if (!id) {
return NextResponse.json({ error: 'ID required' }, { status: 400 })
}
const updatedChange = await prisma.pendingChange.update({
where: { id },
data: {
status,
notes: notes || undefined,
approvedBy: approvedBy || undefined,
approvedAt: approvedAt ? new Date(approvedAt) : undefined,
}
})
return NextResponse.json(mapToPendingChange(updatedChange))
} catch (error) {
console.error('Error updating pending change:', error)
return NextResponse.json({ error: 'Failed to update pendinDELETE function · typescript · L122-L145 (24 LOC)src/app/api/admin/pending/route.ts
export async function DELETE(request: NextRequest) {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { searchParams } = new URL(request.url)
const id = searchParams.get('id')
if (!id) {
return NextResponse.json({ error: 'ID required' }, { status: 400 })
}
await prisma.pendingChange.delete({
where: { id }
})
return NextResponse.json({ success: true })
} catch (error) {
console.error('Error deleting pending change:', error)
return NextResponse.json({ error: 'Failed to delete pending change' }, { status: 500 })
}
}POST function · typescript · L7-L150 (144 LOC)src/app/api/admin/publish/route.ts
export async function POST(request: NextRequest) {
try {
// Check authentication and authorization
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
if (!canPublish(user)) {
return NextResponse.json({ error: 'Forbidden - Super Admin only' }, { status: 403 })
}
const { author = user.name || 'Admin' } = await request.json()
// Get approved changes from database
const approvedChanges = await prisma.pendingChange.findMany({
where: { status: 'approved' }
})
if (approvedChanges.length === 0) {
return NextResponse.json({
error: 'No approved changes to publish'
}, { status: 400 })
}
// Load current employees from database
const currentEmployees = await prisma.employee.findMany()
const employeeMap = new Map(currentEmployees.map(e => [e.id, e]))
// Create version snapshot BEFORE applying changes
const versioPOST function · typescript · L7-L107 (101 LOC)src/app/api/admin/rollback/route.ts
export async function POST(request: NextRequest) {
try {
// Check authentication and authorization
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
if (!canPublish(user)) {
return NextResponse.json({ error: 'Forbidden - Super Admin only' }, { status: 403 })
}
const { versionId, author = user.name || 'Admin' } = await request.json()
if (!versionId) {
return NextResponse.json({ error: 'Version ID required' }, { status: 400 })
}
// Find the version in database
const version = await prisma.version.findFirst({
where: { versionId }
})
if (!version) {
return NextResponse.json({ error: 'Version not found' }, { status: 404 })
}
// Get current employees for backup
const currentEmployees = await prisma.employee.findMany()
const currentSnapshot = currentEmployees.map(emp => ({
id: emp.id,
firstName: emp.fAll rows above produced by Repobility · https://repobility.com
GET function · typescript · L4-L23 (20 LOC)src/app/api/admin/session/route.ts
export async function GET() {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json(
{ error: 'Not authenticated' },
{ status: 401 }
)
}
return NextResponse.json(user)
} catch (error) {
console.error('Session error:', error)
return NextResponse.json(
{ error: 'Failed to get session' },
{ status: 500 }
)
}
}GET function · typescript · L15-L55 (41 LOC)src/app/api/admin/sync/mappings/route.ts
export async function GET() {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const mappings = await prisma.syncMapping.findMany({
orderBy: { addedAt: 'desc' },
})
// Batch-fetch employee names for all mapped IDs
const employeeIds = mappings.map((m) => m.employeeId)
const employees = await prisma.employee.findMany({
where: { id: { in: employeeIds } },
select: { id: true, firstName: true, lastName: true },
})
const employeeMap = new Map(
employees.map((e) => [e.id, `${e.firstName} ${e.lastName}`])
)
return NextResponse.json(
mappings.map((m) => ({
id: m.id,
nextivaEmail: m.nextivaEmail,
employeeId: m.employeeId,
employeeName: employeeMap.get(m.employeeId) ?? '(employee not found)',
addedBy: m.addedBy,
addedAt: m.addedAt.toISOString(),
notes: m.notes,
}))
POST function · typescript · L57-L133 (77 LOC)src/app/api/admin/sync/mappings/route.ts
export async function POST(request: NextRequest) {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
if (!isSuperAdmin(user)) {
return NextResponse.json(
{ error: 'Forbidden — Superadmin only' },
{ status: 403 }
)
}
const { nextivaEmail, employeeId, notes } = await request.json()
if (!nextivaEmail || !employeeId) {
return NextResponse.json(
{ error: 'Missing required fields: nextivaEmail, employeeId' },
{ status: 400 }
)
}
// Validate that the employee exists
const employee = await prisma.employee.findUnique({
where: { id: employeeId },
select: { id: true, firstName: true, lastName: true },
})
if (!employee) {
return NextResponse.json(
{ error: 'Employee not found' },
{ status: 404 }
)
}
// Check for duplicate mapping
const existing = awaDELETE function · typescript · L135-L177 (43 LOC)src/app/api/admin/sync/mappings/route.ts
export async function DELETE(request: NextRequest) {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
if (!isSuperAdmin(user)) {
return NextResponse.json(
{ error: 'Forbidden — Superadmin only' },
{ status: 403 }
)
}
const { searchParams } = new URL(request.url)
const id = searchParams.get('id')
if (!id) {
return NextResponse.json(
{ error: 'Mapping ID required (pass as ?id=...)' },
{ status: 400 }
)
}
const mapping = await prisma.syncMapping.findUnique({ where: { id } })
if (!mapping) {
return NextResponse.json(
{ error: 'Mapping not found' },
{ status: 404 }
)
}
await prisma.syncMapping.delete({ where: { id } })
return NextResponse.json({ success: true })
} catch (error) {
console.error('Error deleting sync mapping:', error)
return NextRespoGET function · typescript · L16-L57 (42 LOC)src/app/api/admin/sync/status/route.ts
export async function GET() {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const [recentLogs, mappingCount] = await Promise.all([
prisma.syncLog.findMany({
orderBy: { timestamp: 'desc' },
take: 10,
}),
prisma.syncMapping.count(),
])
const lastSync = recentLogs.length > 0 ? recentLogs[0] : null
return NextResponse.json({
lastSyncTimestamp: lastSync?.timestamp.toISOString() ?? null,
mappingCount,
recentLogs: recentLogs.map((log) => ({
id: log.id,
timestamp: log.timestamp.toISOString(),
source: log.source,
totalRows: log.totalRows,
matched: log.matched,
created: log.created,
updated: log.updated,
skipped: log.skipped,
errors: log.errors,
details: log.details,
triggeredBy: log.triggeredBy,
})),
})
} catch (error) {
authenticateRequest function · typescript · L19-L23 (5 LOC)src/app/api/admin/sync/upload/route.ts
async function authenticateRequest(request: NextRequest): Promise<{
authenticated: boolean
triggeredBy: string
source: 'manual' | 'automated'
}> {parseCSVText function · typescript · L68-L105 (38 LOC)src/app/api/admin/sync/upload/route.ts
function parseCSVText(csvText: string): NextivaCSVRow[] {
const result = Papa.parse<NextivaCSVRow>(csvText, {
header: true,
skipEmptyLines: true,
transformHeader: (header) => header.trim(),
})
if (result.errors.length > 0) {
const criticalErrors = result.errors.filter(
(e) => e.type !== 'FieldMismatch'
)
if (criticalErrors.length > 0) {
throw new Error(
`CSV parse errors: ${criticalErrors.map((e) => e.message).join('; ')}`
)
}
}
if (!result.data || result.data.length === 0) {
throw new Error('CSV file is empty or contains no valid data')
}
// Validate that the Name column exists
const firstRow = result.data[0]
if (!('Name' in firstRow)) {
// Try case-insensitive header lookup
const headers = Object.keys(firstRow)
const nameHeader = headers.find((h) => h.toLowerCase().trim() === 'name')
if (!nameHeader) {
throw new Error(
'CSV missing required "Name" column. Found columns: ' +
POST function · typescript · L107-L185 (79 LOC)src/app/api/admin/sync/upload/route.ts
export async function POST(request: NextRequest) {
try {
// --- Authenticate ---
const auth = await authenticateRequest(request)
if (!auth.authenticated) {
return NextResponse.json(
{ error: 'Unauthorized — valid admin session or SYNC_SECRET required' },
{ status: 401 }
)
}
// --- Extract CSV text from request ---
let csvText: string
const contentType = request.headers.get('content-type') || ''
if (contentType.includes('multipart/form-data')) {
// Admin UI upload — FormData with a "file" field
const formData = await request.formData()
const file = formData.get('file')
if (!file || !(file instanceof File)) {
return NextResponse.json(
{ error: 'Missing "file" field in form data' },
{ status: 400 }
)
}
csvText = await file.text()
} else if (
contentType.includes('text/csv') ||
contentType.includes('application/octet-stream')
) {
Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
GET function · typescript · L6-L37 (32 LOC)src/app/api/admin/users/route.ts
export async function GET() {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
if (!isSuperAdmin(user)) {
return NextResponse.json({ error: 'Forbidden - Superadmin only' }, { status: 403 })
}
const users = await prisma.user.findMany({
orderBy: { addedAt: 'desc' }
})
// Map to expected format
const mappedUsers = users.map(u => ({
id: u.id,
email: u.email,
name: u.name,
role: u.role,
addedAt: u.addedAt.toISOString(),
addedBy: u.addedBy
}))
return NextResponse.json(mappedUsers)
} catch (error) {
console.error('Error reading users:', error)
return NextResponse.json({ error: 'Failed to fetch users' }, { status: 500 })
}
}POST function · typescript · L40-L100 (61 LOC)src/app/api/admin/users/route.ts
export async function POST(request: NextRequest) {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
if (!isSuperAdmin(user)) {
return NextResponse.json({ error: 'Forbidden - Superadmin only' }, { status: 403 })
}
const { email, name, role, password } = await request.json()
if (!email || !name || !role || !password) {
return NextResponse.json({ error: 'Missing required fields (email, name, role, password)' }, { status: 400 })
}
if (role !== 'superadmin' && role !== 'approver' && role !== 'editor') {
return NextResponse.json({ error: 'Invalid role' }, { status: 400 })
}
// Check if user already exists
const existingUser = await prisma.user.findUnique({
where: { email }
})
if (existingUser) {
return NextResponse.json({ error: 'User already exists' }, { status: 400 })
}
// Hash the password
constDELETE function · typescript · L103-L143 (41 LOC)src/app/api/admin/users/route.ts
export async function DELETE(request: NextRequest) {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
if (!isSuperAdmin(user)) {
return NextResponse.json({ error: 'Forbidden - Superadmin only' }, { status: 403 })
}
const { id } = await request.json()
if (!id) {
return NextResponse.json({ error: 'User ID required' }, { status: 400 })
}
const userToDelete = await prisma.user.findUnique({
where: { id }
})
if (!userToDelete) {
return NextResponse.json({ error: 'User not found' }, { status: 404 })
}
// Prevent deleting yourself
if (userToDelete.email === user.email) {
return NextResponse.json({ error: 'Cannot delete your own account' }, { status: 400 })
}
await prisma.user.delete({
where: { id }
})
return NextResponse.json({ success: true })
} catch (error) {
console.error('Error GET function · typescript · L6-L39 (34 LOC)src/app/api/admin/versions/route.ts
export async function GET() {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const versions = await prisma.version.findMany({
orderBy: { timestamp: 'desc' },
select: {
versionId: true,
timestamp: true,
author: true,
changeCount: true,
description: true,
// Don't include snapshot to reduce payload
}
})
// Map to expected format
const mappedVersions = versions.map(v => ({
id: v.versionId,
timestamp: v.timestamp.toISOString(),
author: v.author,
changeCount: v.changeCount,
description: v.description
}))
return NextResponse.json(mappedVersions)
} catch (error) {
console.error('Error reading versions:', error)
return NextResponse.json({ error: 'Failed to fetch versions' }, { status: 500 })
}
}GET function · typescript · L9-L25 (17 LOC)src/app/api/employees/[id]/route.ts
export async function GET(
request: NextRequest,
context: RouteContext
) {
try {
const params = await context.params
const employee = await getEmployeeById(params.id)
if (!employee) {
return NextResponse.json({ error: 'Employee not found' }, { status: 404 })
}
return NextResponse.json(employee)
} catch (error) {
return NextResponse.json({ error: 'Failed to fetch employee' }, { status: 500 })
}
}PATCH function · typescript · L27-L49 (23 LOC)src/app/api/employees/[id]/route.ts
export async function PATCH(
request: NextRequest,
context: RouteContext
) {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const params = await context.params
const body = await request.json()
const employee = await updateEmployee(params.id, body)
if (!employee) {
return NextResponse.json({ error: 'Employee not found' }, { status: 404 })
}
return NextResponse.json(employee)
} catch (error) {
return NextResponse.json({ error: 'Failed to update employee' }, { status: 500 })
}
}DELETE function · typescript · L51-L72 (22 LOC)src/app/api/employees/[id]/route.ts
export async function DELETE(
request: NextRequest,
context: RouteContext
) {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const params = await context.params
const success = await deleteEmployee(params.id)
if (!success) {
return NextResponse.json({ error: 'Employee not found' }, { status: 404 })
}
return NextResponse.json({ success: true })
} catch (error) {
return NextResponse.json({ error: 'Failed to delete employee' }, { status: 500 })
}
}GET function · typescript · L5-L12 (8 LOC)src/app/api/employees/route.ts
export async function GET() {
try {
const employees = await getAllEmployees()
return NextResponse.json(employees)
} catch (error) {
return NextResponse.json({ error: 'Failed to fetch employees' }, { status: 500 })
}
}Repobility — the code-quality scanner for AI-generated software · https://repobility.com
POST function · typescript · L14-L28 (15 LOC)src/app/api/employees/route.ts
export async function POST(request: NextRequest) {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const body = await request.json()
const newEmployee = await addEmployee(body)
return NextResponse.json(newEmployee, { status: 201 })
} catch (error) {
return NextResponse.json({ error: 'Failed to create employee' }, { status: 500 })
}
}PUT function · typescript · L30-L44 (15 LOC)src/app/api/employees/route.ts
export async function PUT(request: NextRequest) {
try {
const user = await getSessionFromCookie()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const employees = await request.json()
await saveEmployees(employees)
return NextResponse.json({ success: true })
} catch (error) {
return NextResponse.json({ error: 'Failed to update employees' }, { status: 500 })
}
}GET function · typescript · L9-L32 (24 LOC)src/app/api/internal/allowed-ips/route.ts
export async function GET(request: NextRequest) {
const authHeader = request.headers.get('x-internal-secret')
const internalSecret = process.env.INTERNAL_API_SECRET
if (!internalSecret || authHeader !== internalSecret) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
}
try {
const allowedIPs = await prisma.allowedIP.findMany({
select: { ip: true },
})
const ips = allowedIPs.map((record) => record.ip)
return NextResponse.json({ ips })
} catch (error) {
console.error('Failed to fetch allowed IPs:', error)
return NextResponse.json(
{ error: 'Failed to fetch allowed IPs' },
{ status: 500 }
)
}
}page 1 / 3next ›