fix(prisma): remplace les enums par des String (compat SQLite)
SQLite ne supporte pas les enums Prisma. Les 6 enums (HostStatus, HostSource, Protocol, PortState, ScanType, ScanStatus) sont convertis en champs String avec valeurs par défaut littérales et documentation inline des valeurs autorisées. La validation reste assurée par Zod côté app. Adapte aussi les mappings statusVariant des pages hosts/scans en Record<string, BadgeVariant> avec fallback 'default'. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -49,10 +49,11 @@ model Host {
|
||||
vendor String? // Issue du MAC (OUI) ou d'une fingerprint
|
||||
osGuess String? // OS détecté / renseigné
|
||||
|
||||
status HostStatus @default(UNKNOWN)
|
||||
// Valeurs : "UP" | "DOWN" | "UNKNOWN" (validation Zod côté app)
|
||||
status String @default("UNKNOWN")
|
||||
|
||||
// Origine de la fiche : saisie manuelle ou découverte
|
||||
source HostSource @default(MANUAL)
|
||||
// Origine de la fiche : "MANUAL" | "DISCOVERED" | "IMPORTED"
|
||||
source String @default("MANUAL")
|
||||
|
||||
// Relations
|
||||
networkId String?
|
||||
@@ -71,17 +72,10 @@ model Host {
|
||||
@@index([status])
|
||||
}
|
||||
|
||||
enum HostStatus {
|
||||
UP
|
||||
DOWN
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
enum HostSource {
|
||||
MANUAL
|
||||
DISCOVERED
|
||||
IMPORTED
|
||||
}
|
||||
// NOTE : les enums Prisma ne sont pas supportés par SQLite. On stocke
|
||||
// donc tous les statuts/types en `String` et on valide les valeurs
|
||||
// possibles côté app via Zod. Constantes documentées au-dessus de
|
||||
// chaque champ.
|
||||
|
||||
// =====================================================================
|
||||
// Ports ouverts sur un hôte
|
||||
@@ -89,10 +83,12 @@ enum HostSource {
|
||||
model Port {
|
||||
id String @id @default(cuid())
|
||||
number Int
|
||||
protocol Protocol @default(TCP)
|
||||
// Valeurs : "TCP" | "UDP"
|
||||
protocol String @default("TCP")
|
||||
serviceName String? // ex: "http", "ssh", renseigné ou deviné
|
||||
banner String? // Banner applicatif collecté
|
||||
state PortState @default(OPEN)
|
||||
// Valeurs : "OPEN" | "CLOSED" | "FILTERED" | "UNKNOWN"
|
||||
state String @default("OPEN")
|
||||
|
||||
hostId String
|
||||
host Host @relation(fields: [hostId], references: [id], onDelete: Cascade)
|
||||
@@ -110,17 +106,8 @@ model Port {
|
||||
@@index([number])
|
||||
}
|
||||
|
||||
enum Protocol {
|
||||
TCP
|
||||
UDP
|
||||
}
|
||||
|
||||
enum PortState {
|
||||
OPEN
|
||||
CLOSED
|
||||
FILTERED
|
||||
UNKNOWN
|
||||
}
|
||||
// Protocol : "TCP" | "UDP" — voir Port.protocol
|
||||
// PortState : "OPEN" | "CLOSED" | "FILTERED" | "UNKNOWN" — voir Port.state
|
||||
|
||||
// =====================================================================
|
||||
// Applications (Jellyfin, Home Assistant, Nextcloud, etc.)
|
||||
@@ -160,8 +147,10 @@ model HostApplication {
|
||||
// =====================================================================
|
||||
model Scan {
|
||||
id String @id @default(cuid())
|
||||
type ScanType
|
||||
status ScanStatus @default(PENDING)
|
||||
// Valeurs : "PING" | "PORT" | "ARP" | "MDNS" | "FULL"
|
||||
type String
|
||||
// Valeurs : "PENDING" | "RUNNING" | "COMPLETED" | "FAILED" | "CANCELLED"
|
||||
status String @default("PENDING")
|
||||
target String // CIDR, IP, ou hôte
|
||||
params String? // JSON (Zod validé côté app)
|
||||
startedAt DateTime?
|
||||
@@ -180,21 +169,8 @@ model Scan {
|
||||
@@index([type])
|
||||
}
|
||||
|
||||
enum ScanType {
|
||||
PING
|
||||
PORT
|
||||
ARP
|
||||
MDNS
|
||||
FULL
|
||||
}
|
||||
|
||||
enum ScanStatus {
|
||||
PENDING
|
||||
RUNNING
|
||||
COMPLETED
|
||||
FAILED
|
||||
CANCELLED
|
||||
}
|
||||
// ScanType : "PING" | "PORT" | "ARP" | "MDNS" | "FULL" — voir Scan.type
|
||||
// ScanStatus : "PENDING" | "RUNNING" | "COMPLETED" | "FAILED" | "CANCELLED" — voir Scan.status
|
||||
|
||||
// Résultat individuel d'un scan (rattaché à un hôte quand possible)
|
||||
model ScanResult {
|
||||
|
||||
@@ -30,11 +30,13 @@ async function getHosts() {
|
||||
}
|
||||
}
|
||||
|
||||
const statusVariant = {
|
||||
type BadgeVariant = 'default' | 'success' | 'warning' | 'destructive' | 'info' | 'outline';
|
||||
|
||||
const statusVariant: Record<string, BadgeVariant> = {
|
||||
UP: 'success',
|
||||
DOWN: 'destructive',
|
||||
UNKNOWN: 'default',
|
||||
} as const;
|
||||
};
|
||||
|
||||
export default async function HostsPage() {
|
||||
const hosts = await getHosts();
|
||||
@@ -82,7 +84,7 @@ export default async function HostsPage() {
|
||||
{h.network?.name ?? '—'}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={statusVariant[h.status]}>{h.status}</Badge>
|
||||
<Badge variant={statusVariant[h.status] ?? 'default'}>{h.status}</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="text-muted-foreground">
|
||||
{h.ports.length > 0 ? (
|
||||
|
||||
@@ -26,13 +26,15 @@ async function getScans() {
|
||||
}
|
||||
}
|
||||
|
||||
const statusVariant = {
|
||||
type BadgeVariant = 'default' | 'success' | 'warning' | 'destructive' | 'info' | 'outline';
|
||||
|
||||
const statusVariant: Record<string, BadgeVariant> = {
|
||||
COMPLETED: 'success',
|
||||
RUNNING: 'info',
|
||||
PENDING: 'default',
|
||||
FAILED: 'destructive',
|
||||
CANCELLED: 'warning',
|
||||
} as const;
|
||||
};
|
||||
|
||||
function formatDuration(start: Date | null, end: Date | null) {
|
||||
if (!start || !end) return '—';
|
||||
@@ -83,7 +85,7 @@ export default async function ScansPage() {
|
||||
</TableCell>
|
||||
<TableCell className="font-mono text-sm">{s.target}</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={statusVariant[s.status]}>{s.status}</Badge>
|
||||
<Badge variant={statusVariant[s.status] ?? 'default'}>{s.status}</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="tabular-nums">{s.hostsFound}</TableCell>
|
||||
<TableCell className="tabular-nums">{s.portsFound}</TableCell>
|
||||
|
||||
Reference in New Issue
Block a user