Initial commit: SecourIT Gestion
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { getCurrentUser } from '@/lib/auth'
|
||||
|
||||
export async function GET(request: Request, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const user = await getCurrentUser()
|
||||
if (!user) return NextResponse.json({ error: 'Non authentifi??' }, { status: 401 })
|
||||
|
||||
const metrics: any[] = await prisma.$queryRawUnsafe(`
|
||||
SELECT * FROM "ServerMetric"
|
||||
WHERE "serverId" = $1
|
||||
ORDER BY "timestamp" DESC
|
||||
LIMIT 60
|
||||
`, params.id)
|
||||
|
||||
return NextResponse.json(metrics)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: Request, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const token = searchParams.get('token')
|
||||
const expectedToken = process.env.SERVER_MONITOR_SECRET
|
||||
|
||||
if (expectedToken && token !== expectedToken) {
|
||||
return NextResponse.json({ error: 'Non autoris??' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const metricId = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}`
|
||||
|
||||
await prisma.$executeRawUnsafe(`
|
||||
INSERT INTO "ServerMetric" (id, "serverId", "cpuUsage", "ramUsage", "diskUsage", uptime, "timestamp")
|
||||
VALUES ($1, $2, $3, $4, $5, $6, NOW())
|
||||
`, metricId, params.id,
|
||||
body.cpuUsage != null ? Number(body.cpuUsage) : null,
|
||||
body.ramUsage != null ? Number(body.ramUsage) : null,
|
||||
body.diskUsage != null ? Number(body.diskUsage) : null,
|
||||
body.uptime != null ? BigInt(body.uptime) : null)
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { getCurrentUser } from '@/lib/auth'
|
||||
|
||||
export async function GET(request: Request, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const user = await getCurrentUser()
|
||||
if (!user) return NextResponse.json({ error: 'Non authentifi??' }, { status: 401 })
|
||||
|
||||
const servers: any[] = await prisma.$queryRawUnsafe(`
|
||||
SELECT s.*,
|
||||
(SELECT json_agg(json_build_object(
|
||||
'cpuUsage', sm."cpuUsage",
|
||||
'ramUsage', sm."ramUsage",
|
||||
'diskUsage', sm."diskUsage",
|
||||
'uptime', sm.uptime,
|
||||
'timestamp', sm."timestamp"
|
||||
) ORDER BY sm."timestamp" DESC) FROM "ServerMetric" sm
|
||||
WHERE sm."serverId" = s.id
|
||||
LIMIT 60) as metrics
|
||||
FROM "Server" s
|
||||
WHERE s.id = $1 AND s."userId" = $2
|
||||
`, params.id, user.id)
|
||||
|
||||
if (!servers.length) {
|
||||
return NextResponse.json({ error: 'Serveur introuvable' }, { status: 404 })
|
||||
}
|
||||
|
||||
return NextResponse.json(servers[0])
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export async function PATCH(request: Request, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const user = await getCurrentUser()
|
||||
if (!user) return NextResponse.json({ error: 'Non authentifi??' }, { status: 401 })
|
||||
|
||||
const body = await request.json()
|
||||
const now = new Date()
|
||||
|
||||
const fields: string[] = []
|
||||
const values: any[] = []
|
||||
let idx = 1
|
||||
|
||||
for (const key of ['name', 'ip', 'os', 'cpu', 'ram', 'disk', 'status', 'notes']) {
|
||||
if (body[key] !== undefined) {
|
||||
fields.push(`"${key}" = $${idx++}`)
|
||||
values.push(body[key])
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of ['monthlyCost']) {
|
||||
if (body[key] !== undefined) {
|
||||
fields.push(`"${key}" = $${idx++}`)
|
||||
values.push(Number(body[key]))
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of ['startDate', 'endDate']) {
|
||||
if (body[key] !== undefined) {
|
||||
fields.push(`"${key}" = $${idx++}`)
|
||||
values.push(body[key] ? new Date(body[key]) : null)
|
||||
}
|
||||
}
|
||||
|
||||
if (fields.length === 0) {
|
||||
return NextResponse.json({ error: 'Aucun champ ?? mettre ?? jour' }, { status: 400 })
|
||||
}
|
||||
|
||||
fields.push(`"updatedAt" = $${idx++}`)
|
||||
values.push(now)
|
||||
values.push(params.id)
|
||||
values.push(user.id)
|
||||
|
||||
await prisma.$executeRawUnsafe(`
|
||||
UPDATE "Server" SET ${fields.join(', ')}
|
||||
WHERE id = $${idx} AND "userId" = $${idx + 1}
|
||||
`, ...values)
|
||||
|
||||
const updated: any[] = await prisma.$queryRawUnsafe(`SELECT * FROM "Server" WHERE id = $1`, params.id)
|
||||
|
||||
return NextResponse.json(updated[0])
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE(request: Request, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const user = await getCurrentUser()
|
||||
if (!user) return NextResponse.json({ error: 'Non authentifi??' }, { status: 401 })
|
||||
|
||||
await prisma.$executeRawUnsafe(`
|
||||
DELETE FROM "ServerMetric" WHERE "serverId" = $1
|
||||
`, params.id)
|
||||
|
||||
await prisma.$executeRawUnsafe(`
|
||||
DELETE FROM "Server" WHERE id = $1 AND "userId" = $2
|
||||
`, params.id, user.id)
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { getCurrentUser } from '@/lib/auth'
|
||||
import { exec } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
export async function POST(request: Request, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const token = searchParams.get('token')
|
||||
const expectedToken = process.env.SERVER_MONITOR_SECRET
|
||||
const useToken = expectedToken && token === expectedToken
|
||||
|
||||
if (!useToken) {
|
||||
const user = await getCurrentUser()
|
||||
if (!user) return NextResponse.json({ error: 'Non authentifi??' }, { status: 401 })
|
||||
}
|
||||
|
||||
const serverCheck: any[] = await prisma.$queryRawUnsafe(`SELECT id, name FROM "Server" WHERE id = $1`, params.id)
|
||||
if (!serverCheck.length) return NextResponse.json({ error: 'Serveur introuvable' }, { status: 404 })
|
||||
|
||||
const discovered: any[] = []
|
||||
|
||||
// 1. Systemd services
|
||||
try {
|
||||
const { stdout } = await execAsync(
|
||||
`systemctl list-units --type=service --all --no-legend --no-pager 2>/dev/null | head -50`
|
||||
)
|
||||
for (const line of stdout.trim().split('\n')) {
|
||||
const parts = line.trim().split(/\s+/)
|
||||
if (parts.length < 4) continue
|
||||
const name = parts[0].replace('.service', '')
|
||||
const status = parts[3] === 'running' ? 'running' : parts[3] === 'exited' ? 'stopped' : 'error'
|
||||
const pid = parts[3] === 'running' ? 1 : null
|
||||
|
||||
discovered.push({
|
||||
name,
|
||||
type: 'service',
|
||||
status,
|
||||
processName: parts[0],
|
||||
technology: 'systemd',
|
||||
})
|
||||
}
|
||||
} catch {}
|
||||
|
||||
// 2. Docker containers
|
||||
try {
|
||||
const { stdout } = await execAsync(`docker ps -a --format '{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}' 2>/dev/null | head -30`)
|
||||
for (const line of stdout.trim().split('\n')) {
|
||||
if (!line.trim()) continue
|
||||
const parts = line.trim().split('\t')
|
||||
if (parts.length < 2) continue
|
||||
const name = parts[0]
|
||||
const image = parts[1]
|
||||
const statusText = parts[2] || ''
|
||||
const ports = parts[3] || ''
|
||||
const isRunning = statusText.toLowerCase().includes('up')
|
||||
const port = ports.match(/(\d+)->/) ? parseInt(ports.match(/(\d+)->/)![1]) : null
|
||||
|
||||
discovered.push({
|
||||
name: `Docker: ${name}`,
|
||||
type: 'container',
|
||||
status: isRunning ? 'running' : 'stopped',
|
||||
port,
|
||||
technology: `Docker (${image})`,
|
||||
processName: `docker:${name}`,
|
||||
})
|
||||
}
|
||||
} catch {}
|
||||
|
||||
// 3. Processes listening on ports
|
||||
try {
|
||||
const { stdout } = await execAsync(`ss -tlnp4 2>/dev/null | tail -n +2 | head -30`)
|
||||
for (const line of stdout.trim().split('\n')) {
|
||||
if (!line.trim()) continue
|
||||
const parts = line.trim().split(/\s+/)
|
||||
if (parts.length < 5) continue
|
||||
const addr = parts[4]
|
||||
const port = addr.includes(':') ? parseInt(addr.split(':').pop()!) : null
|
||||
const processInfo = parts[5] || ''
|
||||
const procMatch = processInfo.match(/users:\(\("([^"]+)/)
|
||||
const processName = procMatch ? procMatch[1] : 'unknown'
|
||||
|
||||
if (port && port < 9000 && ![3005, 5432, 3306, 8082, 8086, 8096].includes(port)) {
|
||||
discovered.push({
|
||||
name: `Port ${port}`,
|
||||
type: 'port',
|
||||
status: 'running',
|
||||
port,
|
||||
processName,
|
||||
technology: 'TCP',
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
|
||||
// 4. Nginx sites
|
||||
try {
|
||||
const { stdout } = await execAsync(`ls /etc/nginx/sites-enabled/ 2>/dev/null`)
|
||||
for (const site of stdout.trim().split('\n')) {
|
||||
if (!site.trim()) continue
|
||||
const { stdout: port } = await execAsync(
|
||||
`grep -E 'listen|proxy_pass|server_name' /etc/nginx/sites-enabled/${site} 2>/dev/null | head -5`
|
||||
)
|
||||
discovered.push({
|
||||
name: `Nginx: ${site}`,
|
||||
type: 'website',
|
||||
status: 'running',
|
||||
technology: `nginx\n${port}`,
|
||||
processName: 'nginx',
|
||||
})
|
||||
}
|
||||
} catch {}
|
||||
|
||||
// Insert discovered services
|
||||
const now = new Date()
|
||||
let insertedCount = 0
|
||||
const discoveredNames = new Set(discovered.map(s => s.name))
|
||||
|
||||
for (const svc of discovered) {
|
||||
const existing: any[] = await prisma.$queryRawUnsafe(`
|
||||
SELECT id FROM "Service" WHERE "serverId" = $1 AND name = $2
|
||||
`, params.id, svc.name)
|
||||
|
||||
if (existing.length > 0) {
|
||||
await prisma.$executeRawUnsafe(`
|
||||
UPDATE "Service" SET status = $1, port = $2, technology = $3, "processName" = $4, "updatedAt" = $5
|
||||
WHERE id = $6
|
||||
`, svc.status, svc.port || null, svc.technology || '', svc.processName || '', now, existing[0].id)
|
||||
} else {
|
||||
const id = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}`
|
||||
await prisma.$executeRawUnsafe(`
|
||||
INSERT INTO "Service" (id, "serverId", name, type, port, technology, status, "processName", "createdAt", "updatedAt")
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
`, id, params.id, svc.name, svc.type, svc.port || null, svc.technology || '', svc.status, svc.processName || '', now, now)
|
||||
insertedCount++
|
||||
}
|
||||
}
|
||||
|
||||
// Delete stale services that no longer exist on the server
|
||||
let deleted = 0
|
||||
if (discoveredNames.size > 0) {
|
||||
const namesArr = Array.from(discoveredNames)
|
||||
const placeholders = namesArr.map((_, i) => `$${i + 2}`).join(', ')
|
||||
await prisma.$executeRawUnsafe(`
|
||||
DELETE FROM "Service"
|
||||
WHERE "serverId" = $1 AND "processName" IS NOT NULL AND "processName" != '' AND name NOT IN (${placeholders})
|
||||
`, params.id, ...namesArr)
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
discovered: discovered.length,
|
||||
inserted: insertedCount,
|
||||
deleted,
|
||||
total: discovered.length,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { getCurrentUser } from '@/lib/auth'
|
||||
|
||||
export async function GET(request: Request, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const user = await getCurrentUser()
|
||||
if (!user) return NextResponse.json({ error: 'Non authentifi??' }, { status: 401 })
|
||||
|
||||
const services: any[] = await prisma.$queryRawUnsafe(`
|
||||
SELECT * FROM "Service"
|
||||
WHERE "serverId" = $1
|
||||
ORDER BY type ASC, name ASC
|
||||
`, params.id)
|
||||
|
||||
return NextResponse.json(services)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { getCurrentUser } from '@/lib/auth'
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const user = await getCurrentUser()
|
||||
if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 })
|
||||
|
||||
const servers: any[] = await prisma.$queryRawUnsafe(`
|
||||
SELECT s.*,
|
||||
(SELECT json_build_object(
|
||||
'cpuUsage', sm."cpuUsage",
|
||||
'ramUsage', sm."ramUsage",
|
||||
'diskUsage', sm."diskUsage",
|
||||
'uptime', sm.uptime,
|
||||
'timestamp', sm."timestamp"
|
||||
) FROM "ServerMetric" sm
|
||||
WHERE sm."serverId" = s.id
|
||||
ORDER BY sm."timestamp" DESC LIMIT 1) as latest_metric
|
||||
FROM "Server" s
|
||||
WHERE s."userId" = $1
|
||||
ORDER BY s.name ASC
|
||||
`, user.id)
|
||||
|
||||
const totalServers = servers.length
|
||||
const activeServers = servers.filter((s: any) => s.status === 'ACTIVE').length
|
||||
const totalMonthlyCost = servers.reduce((sum: number, s: any) => sum + Number(s.monthlyCost || 0), 0)
|
||||
const onlineServers = servers.filter((s: any) => s.latest_metric !== null).length
|
||||
|
||||
return NextResponse.json({
|
||||
servers,
|
||||
stats: {
|
||||
totalServers,
|
||||
activeServers,
|
||||
totalMonthlyCost,
|
||||
onlineServers,
|
||||
offlineServers: activeServers - onlineServers,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { getCurrentUser } from '@/lib/auth'
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const user = await getCurrentUser()
|
||||
if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 })
|
||||
|
||||
const servers: any[] = await prisma.$queryRawUnsafe(`
|
||||
SELECT s.*,
|
||||
(SELECT json_build_object(
|
||||
'cpuUsage', sm."cpuUsage",
|
||||
'ramUsage', sm."ramUsage",
|
||||
'diskUsage', sm."diskUsage",
|
||||
'uptime', sm.uptime,
|
||||
'timestamp', sm."timestamp"
|
||||
) FROM "ServerMetric" sm
|
||||
WHERE sm."serverId" = s.id
|
||||
ORDER BY sm."timestamp" DESC LIMIT 1) as latest_metric
|
||||
FROM "Server" s
|
||||
WHERE s."userId" = $1
|
||||
ORDER BY s.name ASC
|
||||
`, user.id)
|
||||
|
||||
const parsed = servers.map((s: any) => ({
|
||||
...s,
|
||||
latest_metric: s.latest_metric || null,
|
||||
}))
|
||||
|
||||
return NextResponse.json(parsed)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const user = await getCurrentUser()
|
||||
if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 })
|
||||
|
||||
const body = await request.json()
|
||||
const id = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}`
|
||||
const now = new Date()
|
||||
|
||||
const { name, ip, os, cpu, ram, disk, startDate, endDate, monthlyCost, status, notes } = body
|
||||
|
||||
await prisma.$executeRawUnsafe(`
|
||||
INSERT INTO "Server" (id, "userId", name, ip, os, cpu, ram, disk, "startDate", "endDate", "monthlyCost", status, notes, "createdAt", "updatedAt")
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
|
||||
`, id, user.id, name || '', ip || '', os || '', cpu || '', ram || '', disk || '',
|
||||
startDate ? new Date(startDate) : null, endDate ? new Date(endDate) : null,
|
||||
monthlyCost ? Number(monthlyCost) : 0, status || 'ACTIVE', notes || '', now, now)
|
||||
|
||||
const created: any[] = await prisma.$queryRawUnsafe(`SELECT * FROM "Server" WHERE id = $1`, id)
|
||||
|
||||
return NextResponse.json(created[0])
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user