Initial commit: SecourIT Gestion

This commit is contained in:
2026-08-23 09:06:18 +00:00
commit cab93c7f7c
85 changed files with 15801 additions and 0 deletions
+51
View File
@@ -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 })
}
}
+110
View File
@@ -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 })
}
}
+45
View File
@@ -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 })
}
}
+63
View File
@@ -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 })
}
}