Initial commit
This commit is contained in:
58
app/api/admin/add-user/route.ts
Normal file
58
app/api/admin/add-user/route.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { safeQuery } from "@/lib/database"
|
||||
import { hashPassword } from "@/lib/auth"
|
||||
import { type UserRole, DEPARTMENTS, TEAMS, FIELDS } from "@/types/user"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { name, isAdmin, field, department, team, role } = await request.json()
|
||||
|
||||
// Input validation
|
||||
if (!name || !field || !department || !team) {
|
||||
return NextResponse.json({ error: "נתונים חסרים" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Validate department, team, and field
|
||||
if (!FIELDS.includes(field as any)) {
|
||||
return NextResponse.json({ error: "תחום לא תקין" }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!DEPARTMENTS.includes(department as any)) {
|
||||
return NextResponse.json({ error: "מסגרת לא תקינה" }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!TEAMS.includes(team as any)) {
|
||||
return NextResponse.json({ error: "צוות לא תקין" }, { status: 400 })
|
||||
}
|
||||
|
||||
const validRoles: UserRole[] = ["user", "team_admin", "department_admin", "field_admin", "global_admin"]
|
||||
|
||||
// Set role based on isAdmin flag or explicit role
|
||||
const userRole: UserRole = (role as UserRole) || (isAdmin ? "global_admin" : "user")
|
||||
|
||||
if (!validRoles.includes(userRole)) {
|
||||
return NextResponse.json({ error: "תפקיד לא תקין" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Generate unique Login ID
|
||||
const { generateUniqueIsraeliID } = await import("@/lib/auth")
|
||||
const nationalId = await generateUniqueIsraeliID()
|
||||
|
||||
// Hash default password "password123"
|
||||
const hashedPassword = await hashPassword("password123")
|
||||
|
||||
await safeQuery(
|
||||
"INSERT INTO users (national_id, password, name, is_admin, role, must_change_password, field, department, team) VALUES (?, ?, ?, ?, ?, TRUE, ?, ?, ?)",
|
||||
[nationalId, hashedPassword, name, isAdmin, userRole, field, department, team],
|
||||
)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
nationalId: nationalId,
|
||||
message: `משתמש ${name} נוסף בהצלחה עם מזהה: ${nationalId}`,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Add user error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בהוספת משתמש" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
76
app/api/admin/change-role/route.ts
Normal file
76
app/api/admin/change-role/route.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { safeQuery } from "@/lib/database"
|
||||
import { type UserRole, ROLE_HIERARCHY, ROLE_NAMES } from "@/types/user"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { adminId, targetUserId, newRole } = await request.json()
|
||||
|
||||
if (!adminId || !targetUserId || !newRole) {
|
||||
return NextResponse.json({ error: "נתונים חסרים" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin data
|
||||
const adminData = (await safeQuery("SELECT role, field, department, team FROM users WHERE national_id = ?", [
|
||||
adminId,
|
||||
])) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const admin = adminData[0]
|
||||
const adminLevel = ROLE_HIERARCHY[admin.role as UserRole] || 0
|
||||
|
||||
// Get target user data
|
||||
const targetData = (await safeQuery("SELECT role, field, department, team, name FROM users WHERE national_id = ?", [
|
||||
targetUserId,
|
||||
])) as any[]
|
||||
|
||||
if (targetData.length === 0) {
|
||||
return NextResponse.json({ error: "משתמש לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const target = targetData[0]
|
||||
const newRoleLevel = ROLE_HIERARCHY[newRole as UserRole] || 0
|
||||
|
||||
// Check if admin has permission to change this role
|
||||
if (adminLevel <= newRoleLevel) {
|
||||
return NextResponse.json({ error: "אין הרשאה לתת תפקיד זה" }, { status: 403 })
|
||||
}
|
||||
|
||||
// Check if admin can manage this user based on hierarchy
|
||||
let canManage = false
|
||||
|
||||
if (admin.role === "global_admin") {
|
||||
canManage = true
|
||||
} else if (admin.role === "field_admin" && admin.field === target.field) {
|
||||
canManage = true
|
||||
} else if (admin.role === "department_admin" && admin.department === target.department) {
|
||||
canManage = true
|
||||
} else if (admin.role === "team_admin" && admin.team === target.team) {
|
||||
canManage = true
|
||||
}
|
||||
|
||||
if (!canManage) {
|
||||
return NextResponse.json({ error: "אין הרשאה לנהל משתמש זה" }, { status: 403 })
|
||||
}
|
||||
|
||||
// Update user role
|
||||
await safeQuery("UPDATE users SET role = ? WHERE national_id = ?", [newRole, targetUserId])
|
||||
|
||||
// Log the action
|
||||
await safeQuery(
|
||||
'INSERT INTO admin_actions (admin_id, action_type, target_user_id, target_role) VALUES (?, "role_change", ?, ?)',
|
||||
[adminId, targetUserId, newRole],
|
||||
)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: `תפקיד ${target.name} שונה ל${ROLE_NAMES[newRole as UserRole]}`,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Change role error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בשינוי תפקיד" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
54
app/api/admin/db-health/route.ts
Normal file
54
app/api/admin/db-health/route.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { healthCheck, getPoolStats } from "@/lib/database"
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const health = await healthCheck()
|
||||
const poolStats = getPoolStats()
|
||||
|
||||
return NextResponse.json({
|
||||
...health,
|
||||
poolStats,
|
||||
recommendations: generateRecommendations(poolStats),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Database health check error:", error)
|
||||
return NextResponse.json(
|
||||
{
|
||||
status: "error",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
{ status: 500 },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function generateRecommendations(stats: any) {
|
||||
const recommendations = []
|
||||
|
||||
if (!stats) {
|
||||
recommendations.push("Unable to get pool statistics")
|
||||
return recommendations
|
||||
}
|
||||
|
||||
const utilizationRate = (stats.totalConnections - stats.freeConnections) / stats.connectionLimit
|
||||
|
||||
if (utilizationRate > 0.8) {
|
||||
recommendations.push("High connection utilization - consider increasing connection limit")
|
||||
}
|
||||
|
||||
if (stats.acquiringConnections > 5) {
|
||||
recommendations.push("Many connections waiting - possible connection leak or high load")
|
||||
}
|
||||
|
||||
if (stats.freeConnections === 0) {
|
||||
recommendations.push("No free connections available - increase pool size or check for connection leaks")
|
||||
}
|
||||
|
||||
if (recommendations.length === 0) {
|
||||
recommendations.push("Database pool is healthy")
|
||||
}
|
||||
|
||||
return recommendations
|
||||
}
|
||||
79
app/api/admin/department-reset/route.ts
Normal file
79
app/api/admin/department-reset/route.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { safeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { adminId } = await request.json()
|
||||
|
||||
if (!adminId) {
|
||||
return NextResponse.json({ error: "מזהה מנהל חסר" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin's field and department
|
||||
const adminData = (await safeQuery(
|
||||
"SELECT field, department FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
||||
[adminId],
|
||||
)) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const { field: adminField, department: adminDepartment } = adminData[0]
|
||||
|
||||
if (!adminField || !adminDepartment) {
|
||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום ומסגרת" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Check cooldown for department resets
|
||||
const lastReset = (await safeQuery(
|
||||
'SELECT timestamp FROM admin_actions WHERE action_type = "reset_department" AND admin_id = ? ORDER BY timestamp DESC LIMIT 1',
|
||||
[adminId],
|
||||
)) as any[]
|
||||
|
||||
if (lastReset.length > 0) {
|
||||
const lastResetTime = new Date(lastReset[0].timestamp).getTime()
|
||||
const now = new Date().getTime()
|
||||
const cooldownMs = 90 * 10 // 1.5 minutes for department resets
|
||||
|
||||
if (now - lastResetTime < cooldownMs) {
|
||||
return NextResponse.json({ error: "יש להמתין 90 שניות בין איפוסי מסגרת" }, { status: 429 })
|
||||
}
|
||||
}
|
||||
|
||||
// Reset department users' statuses with field and department context, but exclude locked users
|
||||
await safeQuery(
|
||||
"UPDATE users SET in_shelter = NULL, last_updated = NULL WHERE field = ? AND department = ? AND lock_status = FALSE",
|
||||
[adminField, adminDepartment],
|
||||
)
|
||||
|
||||
// Get count of locked users that were skipped
|
||||
const lockedUsers = (await safeQuery(
|
||||
"SELECT COUNT(*) as count FROM users WHERE field = ? AND department = ? AND lock_status = TRUE",
|
||||
[adminField, adminDepartment],
|
||||
)) as any[]
|
||||
const lockedCount = lockedUsers[0]?.count || 0
|
||||
|
||||
// Log the action
|
||||
await safeQuery(
|
||||
'INSERT INTO admin_actions (admin_id, action_type, target_user_id) VALUES (?, "reset_department", NULL)',
|
||||
[adminId],
|
||||
)
|
||||
|
||||
let message = `כל הסטטוסים של מסגרת ${adminDepartment} בתחום ${adminField} אופסו בהצלחה`
|
||||
if (lockedCount > 0) {
|
||||
message += ` (${lockedCount} משתמשים נעולים לא אופסו)`
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
field: adminField,
|
||||
department: adminDepartment,
|
||||
message,
|
||||
lockedCount,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Department reset error:", error)
|
||||
return NextResponse.json({ error: "שגיאה באיפוס המסגרת" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
52
app/api/admin/department-stats/route.ts
Normal file
52
app/api/admin/department-stats/route.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { executeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { adminId } = await request.json()
|
||||
|
||||
if (!adminId) {
|
||||
return NextResponse.json({ error: "מזהה מנהל חסר" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin's field and department
|
||||
const adminData = (await executeQuery(
|
||||
"SELECT field, department FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
||||
[adminId],
|
||||
)) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const { field: adminField, department: adminDepartment } = adminData[0]
|
||||
|
||||
if (!adminField || !adminDepartment) {
|
||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום ומסגרת" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get department stats with field and department context
|
||||
const results = (await executeQuery(
|
||||
`
|
||||
SELECT
|
||||
SUM(CASE WHEN in_shelter IS NULL THEN 1 ELSE 0 END) as no_report,
|
||||
SUM(CASE WHEN in_shelter = 'yes' THEN 1 ELSE 0 END) as in_shelter,
|
||||
SUM(CASE WHEN in_shelter = 'no' THEN 1 ELSE 0 END) as not_in_shelter,
|
||||
SUM(CASE WHEN in_shelter = 'no_alarm' THEN 1 ELSE 0 END) as no_alarm,
|
||||
SUM(CASE WHEN in_shelter = 'safe_after_exit' THEN 1 ELSE 0 END) as safe_after_exit
|
||||
FROM users
|
||||
WHERE field = ? AND department = ?
|
||||
`,
|
||||
[adminField, adminDepartment],
|
||||
)) as any[]
|
||||
|
||||
return NextResponse.json({
|
||||
...results[0],
|
||||
field: adminField,
|
||||
department: adminDepartment,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Department stats error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בטעינת סטטיסטיקות המסגרת" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
62
app/api/admin/department-users-by-category/route.ts
Normal file
62
app/api/admin/department-users-by-category/route.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { executeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { adminId, category } = await request.json()
|
||||
|
||||
if (!adminId || !category) {
|
||||
return NextResponse.json({ error: "נתונים חסרים" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin's field and department
|
||||
const adminData = (await executeQuery(
|
||||
"SELECT field, department FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
||||
[adminId],
|
||||
)) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const { field: adminField, department: adminDepartment } = adminData[0]
|
||||
|
||||
if (!adminField || !adminDepartment) {
|
||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום ומסגרת" }, { status: 400 })
|
||||
}
|
||||
|
||||
let query = ""
|
||||
const params: any[] = [adminField, adminDepartment]
|
||||
|
||||
switch (category) {
|
||||
case "no_report":
|
||||
query =
|
||||
"SELECT national_id, name, team FROM users WHERE field = ? AND department = ? AND in_shelter IS NULL ORDER BY team, name"
|
||||
break
|
||||
case "in_shelter":
|
||||
query =
|
||||
"SELECT national_id, name, team FROM users WHERE field = ? AND department = ? AND in_shelter = 'yes' ORDER BY team, name"
|
||||
break
|
||||
case "not_in_shelter":
|
||||
query =
|
||||
"SELECT national_id, name, team FROM users WHERE field = ? AND department = ? AND in_shelter = 'no' ORDER BY team, name"
|
||||
break
|
||||
case "no_alarm":
|
||||
query =
|
||||
"SELECT national_id, name, team FROM users WHERE field = ? AND department = ? AND in_shelter = 'no_alarm' ORDER BY team, name"
|
||||
break
|
||||
case "safe_after_exit":
|
||||
query =
|
||||
"SELECT national_id, name , team FROM users WHERE field = ? AND department = ? AND in_shelter = 'safe_after_exit' ORDER BY name"
|
||||
break
|
||||
default:
|
||||
return NextResponse.json({ error: "קטגוריה לא תקינה" }, { status: 400 })
|
||||
}
|
||||
|
||||
const users = (await executeQuery(query, params)) as any[]
|
||||
return NextResponse.json(users)
|
||||
} catch (error) {
|
||||
console.error("Get department users by category error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בטעינת משתמשי המסגרת" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
58
app/api/admin/department-users/route.ts
Normal file
58
app/api/admin/department-users/route.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { executeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { adminId } = await request.json()
|
||||
|
||||
if (!adminId) {
|
||||
return NextResponse.json({ error: "מזהה מנהל חסר" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin's field and department
|
||||
const adminData = (await executeQuery(
|
||||
"SELECT field, department FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
||||
[adminId],
|
||||
)) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const { field: adminField, department: adminDepartment } = adminData[0]
|
||||
|
||||
if (!adminField || !adminDepartment) {
|
||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום ומסגרת" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get department users with field and department context
|
||||
const users = (await executeQuery(
|
||||
`
|
||||
SELECT
|
||||
national_id,
|
||||
name,
|
||||
in_shelter,
|
||||
last_updated,
|
||||
is_admin,
|
||||
must_change_password,
|
||||
field,
|
||||
department,
|
||||
team,
|
||||
lock_status
|
||||
FROM users
|
||||
WHERE field = ? AND department = ?
|
||||
ORDER BY team, name
|
||||
`,
|
||||
[adminField, adminDepartment],
|
||||
)) as any[]
|
||||
|
||||
return NextResponse.json({
|
||||
users,
|
||||
field: adminField,
|
||||
department: adminDepartment,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Department users error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בטעינת משתמשי המסגרת" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
61
app/api/admin/events/route.ts
Normal file
61
app/api/admin/events/route.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import type { NextRequest } from "next/server"
|
||||
|
||||
// Store active connections
|
||||
const connections = new Set<ReadableStreamDefaultController>()
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const stream = new ReadableStream({
|
||||
start(controller) {
|
||||
connections.add(controller)
|
||||
|
||||
// Send initial connection message
|
||||
controller.enqueue(`data: ${JSON.stringify({ type: "connected", timestamp: new Date().toISOString() })}\n\n`)
|
||||
|
||||
// Keep connection alive with periodic pings
|
||||
const pingInterval = setInterval(() => {
|
||||
try {
|
||||
controller.enqueue(`data: ${JSON.stringify({ type: "ping", timestamp: new Date().toISOString() })}\n\n`)
|
||||
} catch (err) {
|
||||
clearInterval(pingInterval)
|
||||
connections.delete(controller)
|
||||
}
|
||||
}, 30000)
|
||||
|
||||
// Clean up on close
|
||||
request.signal.addEventListener("abort", () => {
|
||||
clearInterval(pingInterval)
|
||||
connections.delete(controller)
|
||||
try {
|
||||
controller.close()
|
||||
} catch (err) {
|
||||
// Connection already closed
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
return new Response(stream, {
|
||||
headers: {
|
||||
"Content-Type": "text/event-stream",
|
||||
"Cache-Control": "no-cache",
|
||||
Connection: "keep-alive",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Headers": "Cache-Control",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Function to broadcast updates to all connected clients
|
||||
export function broadcastUpdate(data: any) {
|
||||
const message = `data: ${JSON.stringify({ type: "update", data, timestamp: new Date().toISOString() })}\n\n`
|
||||
|
||||
connections.forEach((controller) => {
|
||||
try {
|
||||
controller.enqueue(message)
|
||||
} catch (err) {
|
||||
connections.delete(controller)
|
||||
}
|
||||
})
|
||||
}
|
||||
70
app/api/admin/field-reset/route.ts
Normal file
70
app/api/admin/field-reset/route.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { safeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { adminId } = await request.json()
|
||||
|
||||
if (!adminId) {
|
||||
return NextResponse.json({ error: "מזהה מנהל חסר" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin's field
|
||||
const adminData = (await safeQuery("SELECT field FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'", [
|
||||
adminId,
|
||||
])) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const adminField = adminData[0].field
|
||||
|
||||
if (!adminField) {
|
||||
return NextResponse.json({ error: "למנהל לא הוגדר תחום" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Check cooldown for field resets
|
||||
const lastReset = (await safeQuery(
|
||||
'SELECT timestamp FROM admin_actions WHERE action_type = "reset_field" AND admin_id = ? ORDER BY timestamp DESC LIMIT 1',
|
||||
[adminId],
|
||||
)) as any[]
|
||||
|
||||
if (lastReset.length > 0) {
|
||||
const lastResetTime = new Date(lastReset[0].timestamp).getTime()
|
||||
const now = new Date().getTime()
|
||||
const cooldownMs = 2 * 60 * 10 // 2 minutes for field resets
|
||||
|
||||
if (now - lastResetTime < cooldownMs) {
|
||||
return NextResponse.json({ error: "יש להמתין 2 דקות בין איפוסי תחום" }, { status: 429 })
|
||||
}
|
||||
}
|
||||
|
||||
// Reset field users' statuses, but exclude locked users
|
||||
await safeQuery("UPDATE users SET in_shelter = NULL, last_updated = NULL WHERE field = ? AND lock_status = FALSE", [
|
||||
adminField,
|
||||
])
|
||||
|
||||
// Get count of locked users that were skipped
|
||||
const lockedUsers = (await safeQuery("SELECT COUNT(*) as count FROM users WHERE field = ? AND lock_status = TRUE", [
|
||||
adminField,
|
||||
])) as any[]
|
||||
const lockedCount = lockedUsers[0]?.count || 0
|
||||
|
||||
// Log the action
|
||||
await safeQuery(
|
||||
'INSERT INTO admin_actions (admin_id, action_type, target_user_id) VALUES (?, "reset_field", NULL)',
|
||||
[adminId],
|
||||
)
|
||||
|
||||
let message = `כל הסטטוסים של תחום ${adminField} אופסו בהצלחה`
|
||||
if (lockedCount > 0) {
|
||||
message += ` (${lockedCount} משתמשים נעולים לא אופסו)`
|
||||
}
|
||||
|
||||
return NextResponse.json({ success: true, field: adminField, message, lockedCount })
|
||||
} catch (error) {
|
||||
console.error("Field reset error:", error)
|
||||
return NextResponse.json({ error: "שגיאה באיפוס התחום" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
50
app/api/admin/field-stats/route.ts
Normal file
50
app/api/admin/field-stats/route.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { executeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { adminId } = await request.json()
|
||||
|
||||
if (!adminId) {
|
||||
return NextResponse.json({ error: "מזהה מנהל חסר" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin's field
|
||||
const adminData = (await executeQuery("SELECT field FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'", [
|
||||
adminId,
|
||||
])) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const adminField = adminData[0].field
|
||||
|
||||
if (!adminField) {
|
||||
return NextResponse.json({ error: "למנהל לא הוגדר תחום" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get field stats
|
||||
const results = (await executeQuery(
|
||||
`
|
||||
SELECT
|
||||
SUM(CASE WHEN in_shelter IS NULL THEN 1 ELSE 0 END) as no_report,
|
||||
SUM(CASE WHEN in_shelter = 'yes' THEN 1 ELSE 0 END) as in_shelter,
|
||||
SUM(CASE WHEN in_shelter = 'no' THEN 1 ELSE 0 END) as not_in_shelter,
|
||||
SUM(CASE WHEN in_shelter = 'no_alarm' THEN 1 ELSE 0 END) as no_alarm,
|
||||
SUM(CASE WHEN in_shelter = 'safe_after_exit' THEN 1 ELSE 0 END) as safe_after_exit
|
||||
FROM users
|
||||
WHERE field = ?
|
||||
`,
|
||||
[adminField],
|
||||
)) as any[]
|
||||
|
||||
return NextResponse.json({
|
||||
...results[0],
|
||||
field: adminField,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Field stats error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בטעינת סטטיסטיקות התחום" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
60
app/api/admin/field-users-by-category/route.ts
Normal file
60
app/api/admin/field-users-by-category/route.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { executeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { adminId, category } = await request.json()
|
||||
|
||||
if (!adminId || !category) {
|
||||
return NextResponse.json({ error: "נתונים חסרים" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin's field
|
||||
const adminData = (await executeQuery("SELECT field FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'", [
|
||||
adminId,
|
||||
])) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const adminField = adminData[0].field
|
||||
|
||||
if (!adminField) {
|
||||
return NextResponse.json({ error: "למנהל לא הוגדר תחום" }, { status: 400 })
|
||||
}
|
||||
|
||||
let query = ""
|
||||
const params: any[] = [adminField]
|
||||
|
||||
switch (category) {
|
||||
case "no_report":
|
||||
query =
|
||||
"SELECT national_id, name, department, team FROM users WHERE field = ? AND in_shelter IS NULL ORDER BY department, team, name"
|
||||
break
|
||||
case "in_shelter":
|
||||
query =
|
||||
"SELECT national_id, name, department, team FROM users WHERE field = ? AND in_shelter = 'yes' ORDER BY department, team, name"
|
||||
break
|
||||
case "not_in_shelter":
|
||||
query =
|
||||
"SELECT national_id, name, department, team FROM users WHERE field = ? AND in_shelter = 'no' ORDER BY department, team, name"
|
||||
break
|
||||
case "no_alarm":
|
||||
query =
|
||||
"SELECT national_id, name, department, team FROM users WHERE field = ? AND in_shelter = 'no_alarm' ORDER BY department, team, name"
|
||||
break
|
||||
case "safe_after_exit":
|
||||
query = "SELECT national_id, name, department, team FROM users WHERE in_shelter = 'safe_after_exit' ORDER BY name"
|
||||
break
|
||||
default:
|
||||
return NextResponse.json({ error: "קטגוריה לא תקינה" }, { status: 400 })
|
||||
}
|
||||
|
||||
const users = (await executeQuery(query, params)) as any[]
|
||||
return NextResponse.json(users)
|
||||
} catch (error) {
|
||||
console.error("Get field users by category error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בטעינת משתמשי התחום" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
56
app/api/admin/field-users/route.ts
Normal file
56
app/api/admin/field-users/route.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { executeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { adminId } = await request.json()
|
||||
|
||||
if (!adminId) {
|
||||
return NextResponse.json({ error: "מזהה מנהל חסר" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin's field
|
||||
const adminData = (await executeQuery("SELECT field FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'", [
|
||||
adminId,
|
||||
])) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const adminField = adminData[0].field
|
||||
|
||||
if (!adminField) {
|
||||
return NextResponse.json({ error: "למנהל לא הוגדר תחום" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get field users
|
||||
const users = (await executeQuery(
|
||||
`
|
||||
SELECT
|
||||
national_id,
|
||||
name,
|
||||
in_shelter,
|
||||
last_updated,
|
||||
is_admin,
|
||||
must_change_password,
|
||||
field,
|
||||
department,
|
||||
team,
|
||||
lock_status
|
||||
FROM users
|
||||
WHERE field = ?
|
||||
ORDER BY department, team, name
|
||||
`,
|
||||
[adminField],
|
||||
)) as any[]
|
||||
|
||||
return NextResponse.json({
|
||||
users,
|
||||
field: adminField,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Field users error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בטעינת משתמשי התחום" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
28
app/api/admin/last-reset/route.ts
Normal file
28
app/api/admin/last-reset/route.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { executeQuery } from "@/lib/database"
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const results = (await executeQuery(`
|
||||
SELECT aa.timestamp, u.name
|
||||
FROM admin_actions aa
|
||||
JOIN users u ON aa.admin_id = u.national_id
|
||||
WHERE aa.action_type = 'reset_all'
|
||||
ORDER BY aa.timestamp DESC
|
||||
LIMIT 1
|
||||
`)) as any[]
|
||||
|
||||
if (results.length > 0) {
|
||||
const result = results[0]
|
||||
return NextResponse.json({
|
||||
lastReset: `${result.name} - ${new Date(result.timestamp).toLocaleString("he-IL")}`,
|
||||
timestamp: result.timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
return NextResponse.json({ lastReset: null })
|
||||
} catch (error) {
|
||||
console.error("Last reset error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בטעינת נתונים" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
122
app/api/admin/manageable-users/route.ts
Normal file
122
app/api/admin/manageable-users/route.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { safeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { adminId } = await request.json()
|
||||
|
||||
if (!adminId) {
|
||||
return NextResponse.json({ error: "מזהה מנהל חסר" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin data
|
||||
const adminData = (await safeQuery("SELECT role, field, department, team FROM users WHERE national_id = ?", [
|
||||
adminId,
|
||||
])) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const admin = adminData[0]
|
||||
let query = ""
|
||||
let params: any[] = []
|
||||
|
||||
// Build query based on admin role
|
||||
if (admin.role === "global_admin") {
|
||||
query = `
|
||||
SELECT
|
||||
national_id,
|
||||
name,
|
||||
role,
|
||||
field,
|
||||
department,
|
||||
team,
|
||||
in_shelter,
|
||||
last_updated,
|
||||
lock_status,
|
||||
is_admin,
|
||||
must_change_password
|
||||
FROM users
|
||||
ORDER BY field, department, team, name
|
||||
`
|
||||
} else if (admin.role === "field_admin") {
|
||||
query = `
|
||||
SELECT
|
||||
national_id,
|
||||
name,
|
||||
role,
|
||||
field,
|
||||
department,
|
||||
team,
|
||||
in_shelter,
|
||||
last_updated,
|
||||
lock_status,
|
||||
is_admin,
|
||||
must_change_password
|
||||
FROM users
|
||||
WHERE field = ?
|
||||
ORDER BY department, team, name
|
||||
`
|
||||
params = [admin.field]
|
||||
} else if (admin.role === "department_admin") {
|
||||
query = `
|
||||
SELECT
|
||||
national_id,
|
||||
name,
|
||||
role,
|
||||
field,
|
||||
department,
|
||||
team,
|
||||
in_shelter,
|
||||
last_updated,
|
||||
lock_status,
|
||||
is_admin,
|
||||
must_change_password
|
||||
FROM users
|
||||
WHERE department = ?
|
||||
ORDER BY team, name
|
||||
`
|
||||
params = [admin.department]
|
||||
} else if (admin.role === "team_admin") {
|
||||
// Team admins can only manage their own team members
|
||||
query = `
|
||||
SELECT
|
||||
national_id,
|
||||
name,
|
||||
role,
|
||||
field,
|
||||
department,
|
||||
team,
|
||||
in_shelter,
|
||||
last_updated,
|
||||
lock_status,
|
||||
is_admin,
|
||||
must_change_password
|
||||
FROM users
|
||||
WHERE team = ? AND role = 'user'
|
||||
ORDER BY name
|
||||
`
|
||||
params = [admin.team]
|
||||
} else {
|
||||
return NextResponse.json({ error: "אין הרשאות ניהול" }, { status: 403 })
|
||||
}
|
||||
|
||||
const users = (await safeQuery(query, params)) as any[]
|
||||
|
||||
console.log("Manageable users query result:", users) // Debug log
|
||||
|
||||
return NextResponse.json({
|
||||
users,
|
||||
adminRole: admin.role,
|
||||
scope: {
|
||||
field: admin.field,
|
||||
department: admin.department,
|
||||
team: admin.team,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Get manageable users error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בטעינת משתמשים" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
86
app/api/admin/report-on-behalf/route.ts
Normal file
86
app/api/admin/report-on-behalf/route.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { safeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { adminId, targetUserId, status } = await request.json()
|
||||
|
||||
if (!adminId || !targetUserId || !status) {
|
||||
return NextResponse.json({ error: "נתונים חסרים" }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!["yes", "no", "no_alarm", "safe_after_exit"].includes(status)) {
|
||||
return NextResponse.json({ error: "סטטוס לא תקין" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin data
|
||||
const adminData = (await safeQuery("SELECT role, field, department, team, name FROM users WHERE national_id = ?", [
|
||||
adminId,
|
||||
])) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const admin = adminData[0]
|
||||
|
||||
// Get target user data
|
||||
const targetData = (await safeQuery("SELECT field, department, team, name FROM users WHERE national_id = ?", [
|
||||
targetUserId,
|
||||
])) as any[]
|
||||
|
||||
if (targetData.length === 0) {
|
||||
return NextResponse.json({ error: "משתמש לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const target = targetData[0]
|
||||
|
||||
// Check if admin can manage this user based on hierarchy
|
||||
let canManage = false
|
||||
|
||||
if (admin.role === "global_admin") {
|
||||
canManage = true
|
||||
} else if (admin.role === "field_admin" && admin.field === target.field) {
|
||||
canManage = true
|
||||
} else if (admin.role === "department_admin" && admin.department === target.department) {
|
||||
canManage = true
|
||||
} else if (admin.role === "team_admin" && admin.team === target.team) {
|
||||
canManage = true
|
||||
}
|
||||
|
||||
if (!canManage) {
|
||||
return NextResponse.json({ error: "אין הרשאה לדווח עבור משתמש זה" }, { status: 403 })
|
||||
}
|
||||
|
||||
// Update user status
|
||||
await safeQuery("UPDATE users SET in_shelter = ?, last_updated = NOW() WHERE national_id = ?", [
|
||||
status,
|
||||
targetUserId,
|
||||
])
|
||||
|
||||
// Log the action
|
||||
await safeQuery(
|
||||
'INSERT INTO admin_actions (admin_id, action_type, target_user_id) VALUES (?, "report_on_behalf", ?)',
|
||||
[adminId, targetUserId],
|
||||
)
|
||||
|
||||
const statusText =
|
||||
status === "yes"
|
||||
? "במקלט/חדר מוגן"
|
||||
: status === "no"
|
||||
? "לא במקלט"
|
||||
: status === "no_alarm"
|
||||
? "אין אזעקה"
|
||||
: status === "safe_after_exit"
|
||||
? "בטוח אחרי יציאה מהמקלט"
|
||||
: "לא ידוע"
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: `דווח עבור ${target.name}: ${statusText}`,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Report on behalf error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בדיווח" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
72
app/api/admin/reset-all/route.ts
Normal file
72
app/api/admin/reset-all/route.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { safeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { adminId } = await request.json()
|
||||
|
||||
// Check cooldown
|
||||
const lastReset = (await safeQuery(
|
||||
'SELECT timestamp FROM admin_actions WHERE action_type = "reset_all" ORDER BY timestamp DESC LIMIT 1',
|
||||
)) as any[]
|
||||
|
||||
if (lastReset.length > 0) {
|
||||
const lastResetTime = new Date(lastReset[0].timestamp).getTime()
|
||||
const now = new Date().getTime()
|
||||
const cooldownMs = 2 * 60 * 1000 // 2 minutes in milliseconds
|
||||
|
||||
const timeSinceReset = now - lastResetTime
|
||||
|
||||
console.log("Reset cooldown check:", {
|
||||
lastResetTime: new Date(lastResetTime).toISOString(),
|
||||
now: new Date(now).toISOString(),
|
||||
timeSinceReset: timeSinceReset,
|
||||
cooldownMs: cooldownMs,
|
||||
remainingMs: cooldownMs - timeSinceReset,
|
||||
})
|
||||
|
||||
if (timeSinceReset < cooldownMs) {
|
||||
const remainingSeconds = Math.ceil((cooldownMs - timeSinceReset) / 1000)
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: `יש להמתין ${remainingSeconds} שניות בין איפוסים`,
|
||||
remainingSeconds,
|
||||
cooldownMs,
|
||||
},
|
||||
{ status: 429 },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset ALL users' statuses including admins, but exclude locked users
|
||||
const result = await safeQuery("UPDATE users SET in_shelter = NULL, last_updated = NULL WHERE lock_status = FALSE")
|
||||
|
||||
// Get count of locked users that were skipped
|
||||
const lockedUsers = (await safeQuery("SELECT COUNT(*) as count FROM users WHERE lock_status = TRUE")) as any[]
|
||||
const lockedCount = lockedUsers[0]?.count || 0
|
||||
|
||||
// Log the action
|
||||
await safeQuery('INSERT INTO admin_actions (admin_id, action_type) VALUES (?, "reset_all")', [adminId])
|
||||
|
||||
let message = "כל הסטטוסים אופסו בהצלחה"
|
||||
if (lockedCount > 0) {
|
||||
message += ` (${lockedCount} משתמשים נעולים לא אופסו)`
|
||||
}
|
||||
|
||||
console.log("Reset completed:", {
|
||||
affectedRows: (result as any).affectedRows,
|
||||
lockedCount,
|
||||
adminId,
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message,
|
||||
lockedCount,
|
||||
affectedRows: (result as any).affectedRows,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Reset error:", error)
|
||||
return NextResponse.json({ error: "שגיאה באיפוס" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
56
app/api/admin/reset-password/route.ts
Normal file
56
app/api/admin/reset-password/route.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { safeQuery } from "@/lib/database"
|
||||
import { hashPassword } from "@/lib/auth"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { adminId, targetUserId } = await request.json()
|
||||
|
||||
// Input validation
|
||||
if (!adminId || !targetUserId) {
|
||||
return NextResponse.json({ error: "נתונים חסרים" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Verify admin permissions
|
||||
const admins = await safeQuery(
|
||||
"SELECT role FROM users WHERE national_id = ?",
|
||||
[adminId]
|
||||
) as { role: string | null }[]
|
||||
|
||||
if (
|
||||
admins.length === 0 ||
|
||||
!admins[0].role ||
|
||||
admins[0].role === "user"
|
||||
) {
|
||||
return NextResponse.json({ error: "אין הרשאות מנהל" }, { status: 403 })
|
||||
}
|
||||
|
||||
// Check if target user exists
|
||||
const targetUsers = (await safeQuery("SELECT national_id FROM users WHERE national_id = ?", [
|
||||
targetUserId,
|
||||
])) as any[]
|
||||
|
||||
if (targetUsers.length === 0) {
|
||||
return NextResponse.json({ error: "משתמש לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
// Reset password to "password123"
|
||||
const hashedPassword = await hashPassword("password123")
|
||||
|
||||
await safeQuery(
|
||||
"UPDATE users SET password = ?, must_change_password = TRUE, password_changed_at = NULL WHERE national_id = ?",
|
||||
[hashedPassword, targetUserId],
|
||||
)
|
||||
|
||||
// Log the action
|
||||
await safeQuery(
|
||||
'INSERT INTO admin_actions (admin_id, action_type, target_user_id) VALUES (?, "reset_password", ?)',
|
||||
[adminId, targetUserId],
|
||||
)
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error("Reset password error:", error)
|
||||
return NextResponse.json({ error: "שגיאה באיפוס סיסמה" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
21
app/api/admin/stats/route.ts
Normal file
21
app/api/admin/stats/route.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { executeQuery } from "@/lib/database"
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const results = (await executeQuery(`
|
||||
SELECT
|
||||
SUM(CASE WHEN in_shelter IS NULL THEN 1 ELSE 0 END) as no_report,
|
||||
SUM(CASE WHEN in_shelter = 'yes' THEN 1 ELSE 0 END) as in_shelter,
|
||||
SUM(CASE WHEN in_shelter = 'no' THEN 1 ELSE 0 END) as not_in_shelter,
|
||||
SUM(CASE WHEN in_shelter = 'no_alarm' THEN 1 ELSE 0 END) as no_alarm,
|
||||
SUM(CASE WHEN in_shelter = 'safe_after_exit' THEN 1 ELSE 0 END) as safe_after_exit
|
||||
FROM users
|
||||
`)) as any[]
|
||||
|
||||
return NextResponse.json(results[0])
|
||||
} catch (error) {
|
||||
console.error("Stats error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בטעינת סטטיסטיקות" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
66
app/api/admin/team-reset/route.ts
Normal file
66
app/api/admin/team-reset/route.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { safeQuery } from "@/lib/database"
|
||||
import { revalidatePath } from 'next/cache' // Crucial for Next.js App Router caching
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { adminId } = await request.json()
|
||||
|
||||
if (!adminId) {
|
||||
return NextResponse.json({ error: "מזהה מנהל חסר" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin's field, department, and team
|
||||
const adminData = (await safeQuery(
|
||||
"SELECT field, department, team FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
||||
[adminId],
|
||||
)) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const { field: adminField, department: adminDepartment, team: adminTeam } = adminData[0]
|
||||
|
||||
if (!adminField || !adminDepartment || !adminTeam) {
|
||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום, מסגרת וצוות" }, { status: 400 })
|
||||
}
|
||||
|
||||
await safeQuery(
|
||||
"UPDATE users SET in_shelter = NULL, last_updated = NULL WHERE field = ? AND department = ? AND team = ? AND lock_status = FALSE",
|
||||
[adminField, adminDepartment, adminTeam],
|
||||
)
|
||||
|
||||
// Get count of locked users that were skipped
|
||||
const lockedUsers = (await safeQuery(
|
||||
"SELECT COUNT(*) as count FROM users WHERE field = ? AND department = ? AND team = ? AND lock_status = TRUE",
|
||||
[adminField, adminDepartment, adminTeam],
|
||||
)) as any[]
|
||||
const lockedCount = lockedUsers[0]?.count || 0
|
||||
|
||||
// Log the action
|
||||
await safeQuery(
|
||||
'INSERT INTO admin_actions (admin_id, action_type, target_user_id) VALUES (?, "reset_team", NULL)',
|
||||
[adminId],
|
||||
)
|
||||
|
||||
revalidatePath('/admin')
|
||||
|
||||
let message = `כל הסטטוסים של צוות ${adminTeam} במסגרת ${adminDepartment} בתחום ${adminField} אופסו בהצלחה`
|
||||
if (lockedCount > 0) {
|
||||
message += ` (${lockedCount} משתמשים נעולים לא אופסו)`
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
field: adminField,
|
||||
department: adminDepartment,
|
||||
team: adminTeam,
|
||||
message,
|
||||
lockedCount,
|
||||
}, { status: 200 })
|
||||
} catch (error) {
|
||||
console.error("Team reset error:", error)
|
||||
return NextResponse.json({ error: "שגיאה באיפוס הצוות" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
53
app/api/admin/team-stats/route.ts
Normal file
53
app/api/admin/team-stats/route.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { executeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { adminId } = await request.json()
|
||||
|
||||
if (!adminId) {
|
||||
return NextResponse.json({ error: "מזהה מנהל חסר" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin's field, department, and team
|
||||
const adminData = (await executeQuery(
|
||||
"SELECT field, department, team FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
||||
[adminId],
|
||||
)) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const { field: adminField, department: adminDepartment, team: adminTeam } = adminData[0]
|
||||
|
||||
if (!adminField || !adminDepartment || !adminTeam) {
|
||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום, מסגרת וצוות" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get team stats with full context (field + department + team)
|
||||
const results = (await executeQuery(
|
||||
`
|
||||
SELECT
|
||||
SUM(CASE WHEN in_shelter IS NULL THEN 1 ELSE 0 END) as no_report,
|
||||
SUM(CASE WHEN in_shelter = 'yes' THEN 1 ELSE 0 END) as in_shelter,
|
||||
SUM(CASE WHEN in_shelter = 'no' THEN 1 ELSE 0 END) as not_in_shelter,
|
||||
SUM(CASE WHEN in_shelter = 'no_alarm' THEN 1 ELSE 0 END) as no_alarm,
|
||||
SUM(CASE WHEN in_shelter = 'safe_after_exit' THEN 1 ELSE 0 END) as safe_after_exit
|
||||
FROM users
|
||||
WHERE field = ? AND department = ? AND team = ?
|
||||
`,
|
||||
[adminField, adminDepartment, adminTeam],
|
||||
)) as any[]
|
||||
|
||||
return NextResponse.json({
|
||||
...results[0],
|
||||
field: adminField,
|
||||
department: adminDepartment,
|
||||
team: adminTeam,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Team stats error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בטעינת סטטיסטיקות הצוות" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
62
app/api/admin/team-users-by-category/route.ts
Normal file
62
app/api/admin/team-users-by-category/route.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { executeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { adminId, category } = await request.json()
|
||||
|
||||
if (!adminId || !category) {
|
||||
return NextResponse.json({ error: "נתונים חסרים" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin's field, department, and team
|
||||
const adminData = (await executeQuery(
|
||||
"SELECT field, department, team FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
||||
[adminId],
|
||||
)) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const { field: adminField, department: adminDepartment, team: adminTeam } = adminData[0]
|
||||
|
||||
if (!adminField || !adminDepartment || !adminTeam) {
|
||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום, מסגרת וצוות" }, { status: 400 })
|
||||
}
|
||||
|
||||
let query = ""
|
||||
const params: any[] = [adminField, adminDepartment, adminTeam]
|
||||
|
||||
switch (category) {
|
||||
case "no_report":
|
||||
query =
|
||||
"SELECT national_id, name FROM users WHERE field = ? AND department = ? AND team = ? AND in_shelter IS NULL ORDER BY name"
|
||||
break
|
||||
case "in_shelter":
|
||||
query =
|
||||
"SELECT national_id, name FROM users WHERE field = ? AND department = ? AND team = ? AND in_shelter = 'yes' ORDER BY name"
|
||||
break
|
||||
case "not_in_shelter":
|
||||
query =
|
||||
"SELECT national_id, name FROM users WHERE field = ? AND department = ? AND team = ? AND in_shelter = 'no' ORDER BY name"
|
||||
break
|
||||
case "no_alarm":
|
||||
query =
|
||||
"SELECT national_id, name FROM users WHERE field = ? AND department = ? AND team = ? AND in_shelter = 'no_alarm' ORDER BY name"
|
||||
break
|
||||
case "safe_after_exit":
|
||||
query =
|
||||
"SELECT national_id, name FROM users WHERE field = ? AND department = ? AND team = ? AND in_shelter = 'safe_after_exit' ORDER BY name"
|
||||
break
|
||||
default:
|
||||
return NextResponse.json({ error: "קטגוריה לא תקינה" }, { status: 400 })
|
||||
}
|
||||
|
||||
const users = (await executeQuery(query, params)) as any[]
|
||||
return NextResponse.json(users)
|
||||
} catch (error) {
|
||||
console.error("Get team users by category error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בטעינת משתמשי הצוות" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
58
app/api/admin/team-users/route.ts
Normal file
58
app/api/admin/team-users/route.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { executeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { adminId } = await request.json()
|
||||
|
||||
if (!adminId) {
|
||||
return NextResponse.json({ error: "מזהה מנהל חסר" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin's field, department, and team
|
||||
const adminData = (await executeQuery("SELECT field, department, team FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'", [
|
||||
adminId,
|
||||
])) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const { field: adminField, department: adminDepartment, team: adminTeam } = adminData[0]
|
||||
|
||||
if (!adminField || !adminDepartment || !adminTeam) {
|
||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום, מסגרת וצוות" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get team users with full context (field + department + team)
|
||||
const users = (await executeQuery(
|
||||
`
|
||||
SELECT
|
||||
national_id,
|
||||
name,
|
||||
in_shelter,
|
||||
last_updated,
|
||||
is_admin,
|
||||
must_change_password,
|
||||
field,
|
||||
department,
|
||||
team,
|
||||
lock_status
|
||||
FROM users
|
||||
WHERE field = ? AND department = ? AND team = ?
|
||||
ORDER BY name
|
||||
`,
|
||||
[adminField, adminDepartment, adminTeam],
|
||||
)) as any[]
|
||||
|
||||
return NextResponse.json({
|
||||
users,
|
||||
field: adminField,
|
||||
department: adminDepartment,
|
||||
team: adminTeam,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Team users error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בטעינת משתמשי הצוות" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
74
app/api/admin/toggle-user-lock/route.ts
Normal file
74
app/api/admin/toggle-user-lock/route.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { safeQuery } from "@/lib/database"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { adminId, targetUserId, lockStatus } = await request.json()
|
||||
|
||||
if (!adminId || !targetUserId || typeof lockStatus !== "boolean") {
|
||||
return NextResponse.json({ error: "נתונים חסרים" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get admin data
|
||||
const adminData = (await safeQuery("SELECT role, field, department, team FROM users WHERE national_id = ?", [
|
||||
adminId,
|
||||
])) as any[]
|
||||
|
||||
if (adminData.length === 0) {
|
||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const admin = adminData[0]
|
||||
|
||||
// Get target user data
|
||||
const targetData = (await safeQuery(
|
||||
"SELECT field, department, team, name, lock_status FROM users WHERE national_id = ?",
|
||||
[targetUserId],
|
||||
)) as any[]
|
||||
|
||||
if (targetData.length === 0) {
|
||||
return NextResponse.json({ error: "משתמש לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const target = targetData[0]
|
||||
|
||||
// Check if admin can manage this user based on hierarchy
|
||||
let canManage = false
|
||||
|
||||
if (admin.role === "global_admin") {
|
||||
canManage = true
|
||||
} else if (admin.role === "field_admin" && admin.field === target.field) {
|
||||
canManage = true
|
||||
} else if (admin.role === "department_admin" && admin.department === target.department) {
|
||||
canManage = true
|
||||
} else if (admin.role === "team_admin" && admin.team === target.team) {
|
||||
canManage = true
|
||||
}
|
||||
|
||||
if (!canManage) {
|
||||
return NextResponse.json({ error: "אין הרשאה לנעול/לבטל נעילה של משתמש זה" }, { status: 403 })
|
||||
}
|
||||
|
||||
// Update user lock status
|
||||
await safeQuery("UPDATE users SET lock_status = ? WHERE national_id = ?", [lockStatus, targetUserId])
|
||||
|
||||
// Log the action
|
||||
const actionType = lockStatus ? "lock_user" : "unlock_user"
|
||||
await safeQuery("INSERT INTO admin_actions (admin_id, action_type, target_user_id) VALUES (?, ?, ?)", [
|
||||
adminId,
|
||||
actionType,
|
||||
targetUserId,
|
||||
])
|
||||
|
||||
const statusText = lockStatus ? "נעול" : "לא נעול"
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: `סטטוס נעילה של ${target.name} שונה ל: ${statusText}`,
|
||||
lockStatus,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Toggle user lock error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בשינוי סטטוס נעילה" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
45
app/api/admin/users-by-category/route.ts
Normal file
45
app/api/admin/users-by-category/route.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { executeQuery } from "@/lib/database";
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const category = searchParams.get("category");
|
||||
|
||||
let query = "";
|
||||
// No params needed for these queries, as there are no WHERE conditions
|
||||
// that use parameters other than the in_shelter status itself.
|
||||
let params: any[] = [];
|
||||
|
||||
switch (category) {
|
||||
case "no_report":
|
||||
// Added 'department', 'team', 'field' to SELECT clause
|
||||
query = "SELECT national_id, name, department, team, field FROM users WHERE in_shelter IS NULL ORDER BY name";
|
||||
break;
|
||||
case "in_shelter":
|
||||
// Added 'department', 'team', 'field' to SELECT clause
|
||||
query = "SELECT national_id, name, department, team, field FROM users WHERE in_shelter = 'yes' ORDER BY name";
|
||||
break;
|
||||
case "not_in_shelter":
|
||||
// Added 'department', 'team', 'field' to SELECT clause
|
||||
query = "SELECT national_id, name, department, team, field FROM users WHERE in_shelter = 'no' ORDER BY name";
|
||||
break;
|
||||
case "no_alarm":
|
||||
// Added 'department', 'team', 'field' to SELECT clause
|
||||
query = "SELECT national_id, name, department, team, field FROM users WHERE in_shelter = 'no_alarm' ORDER BY name";
|
||||
break;
|
||||
case "safe_after_exit":
|
||||
// Added 'department', 'team', 'field' to SELECT clause
|
||||
query = "SELECT national_id, name, department, team, field FROM users WHERE in_shelter = 'safe_after_exit' ORDER BY name";
|
||||
break;
|
||||
default:
|
||||
return NextResponse.json({ error: "קטגוריה לא תקינה" }, { status: 400 });
|
||||
}
|
||||
|
||||
const users = (await executeQuery(query, params)) as any[];
|
||||
return NextResponse.json(users);
|
||||
} catch (error) {
|
||||
console.error("Get users by category error:", error);
|
||||
return NextResponse.json({ error: "שגיאה בטעינת משתמשים" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
27
app/api/admin/users/[id]/route.ts
Normal file
27
app/api/admin/users/[id]/route.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { executeQuery } from "@/lib/database"
|
||||
|
||||
export async function DELETE(request: NextRequest, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const nationalId = params.id
|
||||
|
||||
// Check if user exists
|
||||
const users = (await executeQuery("SELECT national_id FROM users WHERE national_id = ?", [nationalId])) as any[]
|
||||
|
||||
if (users.length === 0) {
|
||||
return NextResponse.json({ error: "משתמש לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
// Delete user
|
||||
await executeQuery(
|
||||
"DELETE FROM admin_actions WHERE admin_id = ? OR target_user_id = ?",
|
||||
[nationalId, nationalId]
|
||||
);
|
||||
await executeQuery("DELETE FROM users WHERE national_id = ?", [nationalId])
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error("Delete user error:", error)
|
||||
return NextResponse.json({ error: "שגיאה במחיקת משתמש" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
27
app/api/admin/users/route.ts
Normal file
27
app/api/admin/users/route.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { safeQuery } from "@/lib/database"
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const users = (await safeQuery(`
|
||||
SELECT
|
||||
national_id,
|
||||
name,
|
||||
in_shelter,
|
||||
last_updated,
|
||||
is_admin,
|
||||
must_change_password,
|
||||
field,
|
||||
department,
|
||||
team,
|
||||
lock_status
|
||||
FROM users
|
||||
ORDER BY field, department, team, name
|
||||
`)) as any[]
|
||||
|
||||
return NextResponse.json(users)
|
||||
} catch (error) {
|
||||
console.error("Get users error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בטעינת משתמשים" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
45
app/api/auth/change-password/route.ts
Normal file
45
app/api/auth/change-password/route.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { safeQuery } from "@/lib/database"
|
||||
import { verifyPassword, hashPassword } from "@/lib/auth"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { nationalId, currentPassword, newPassword } = await request.json()
|
||||
|
||||
// Input validation
|
||||
if (!nationalId || !currentPassword || !newPassword) {
|
||||
return NextResponse.json({ error: "נתונים חסרים" }, { status: 400 })
|
||||
}
|
||||
|
||||
if (newPassword.length < 6) {
|
||||
return NextResponse.json({ error: "הסיסמה החדשה חייבת להכיל לפחות 6 תווים" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get current user data
|
||||
const users = (await safeQuery("SELECT password FROM users WHERE national_id = ?", [nationalId])) as any[]
|
||||
|
||||
if (users.length === 0) {
|
||||
return NextResponse.json({ error: "משתמש לא נמצא" }, { status: 404 })
|
||||
}
|
||||
|
||||
const user = users[0]
|
||||
const isValidCurrentPassword = await verifyPassword(currentPassword, user.password)
|
||||
|
||||
if (!isValidCurrentPassword) {
|
||||
return NextResponse.json({ error: "הסיסמה הנוכחית שגויה" }, { status: 401 })
|
||||
}
|
||||
|
||||
// Hash new password and update
|
||||
const hashedNewPassword = await hashPassword(newPassword)
|
||||
|
||||
await safeQuery(
|
||||
"UPDATE users SET password = ?, must_change_password = FALSE, password_changed_at = NOW() WHERE national_id = ?",
|
||||
[hashedNewPassword, nationalId],
|
||||
)
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error("Change password error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בשינוי סיסמה" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
53
app/api/auth/login/route.ts
Normal file
53
app/api/auth/login/route.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { safeQuery } from "@/lib/database"
|
||||
import { validateIsraeliID, verifyPassword } from "@/lib/auth"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { nationalId, password } = await request.json()
|
||||
|
||||
// Input validation
|
||||
if (!nationalId || !password) {
|
||||
return NextResponse.json({ error: "נתונים חסרים" }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!validateIsraeliID(nationalId)) {
|
||||
return NextResponse.json({ error: "מספר תעודת זהות לא תקין" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Use parameterized query to prevent SQL injection
|
||||
const users = (await safeQuery(
|
||||
"SELECT national_id, password, name, is_admin, role, field, department, team, in_shelter, last_updated, must_change_password FROM users WHERE national_id = ?",
|
||||
[nationalId],
|
||||
)) as any[]
|
||||
|
||||
if (users.length === 0) {
|
||||
return NextResponse.json({ error: "משתמש לא נמצא" }, { status: 401 })
|
||||
}
|
||||
|
||||
const user = users[0]
|
||||
const isValidPassword = await verifyPassword(password, user.password)
|
||||
|
||||
if (!isValidPassword) {
|
||||
return NextResponse.json({ error: "סיסמה שגויה" }, { status: 401 })
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
user: {
|
||||
national_id: user.national_id,
|
||||
name: user.name,
|
||||
is_admin: user.is_admin,
|
||||
role: user.role,
|
||||
field: user.field,
|
||||
department: user.department,
|
||||
team: user.team,
|
||||
in_shelter: user.in_shelter,
|
||||
last_updated: user.last_updated,
|
||||
must_change_password: user.must_change_password,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Login error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בשרת" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
28
app/api/status/update/route.ts
Normal file
28
app/api/status/update/route.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { safeQuery } from "@/lib/database"
|
||||
import { broadcastUpdate } from "@/lib/websocket"
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { nationalId, status } = await request.json()
|
||||
|
||||
if (!["yes", "no", "no_alarm", "safe_after_exit"].includes(status)) {
|
||||
return NextResponse.json({ error: "סטטוס לא תקין" }, { status: 400 })
|
||||
}
|
||||
|
||||
await safeQuery("UPDATE users SET in_shelter = ?, last_updated = NOW() WHERE national_id = ?", [status, nationalId])
|
||||
|
||||
// Broadcast the update to all connected admins
|
||||
broadcastUpdate({
|
||||
type: "status_change",
|
||||
user_id: nationalId,
|
||||
status: status,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error("Status update error:", error)
|
||||
return NextResponse.json({ error: "שגיאה בעדכון סטטוס" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
45
app/api/test-db/route.ts
Normal file
45
app/api/test-db/route.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { testConnection, executeQuery, getPoolStats, healthCheck } from "@/lib/database"
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Test basic connection
|
||||
const connectionTest = await testConnection()
|
||||
if (!connectionTest) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "Database connection failed",
|
||||
details: "Could not connect to MySQL database",
|
||||
},
|
||||
{ status: 500 },
|
||||
)
|
||||
}
|
||||
|
||||
// Test query execution with automatic connection management
|
||||
const result = await executeQuery("SELECT COUNT(*) as user_count FROM users")
|
||||
|
||||
// Get pool statistics
|
||||
const poolStats = getPoolStats()
|
||||
|
||||
// Get comprehensive health check
|
||||
const health = await healthCheck()
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: "Database connection successful",
|
||||
userCount: (result as any)[0].user_count,
|
||||
poolStats,
|
||||
health,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Database test error:", error)
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "Database test failed",
|
||||
details: error instanceof Error ? error.message : "Unknown error",
|
||||
},
|
||||
{ status: 500 },
|
||||
)
|
||||
}
|
||||
}
|
||||
6
app/api/websocket/route.ts
Normal file
6
app/api/websocket/route.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { NextRequest } from "next/server"
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
// This will be handled by the custom server
|
||||
return new Response("WebSocket endpoint", { status: 200 })
|
||||
}
|
||||
Reference in New Issue
Block a user