diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma new file mode 100644 index 0000000..22630b6 --- /dev/null +++ b/backend/prisma/schema.prisma @@ -0,0 +1,115 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + passwordHash String @map("password_hash") + displayName String? @map("display_name") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + @@map("users") +} + +model HostGroup { + id Int @id @default(autoincrement()) + name String + parentId Int? @map("parent_id") + sortOrder Int @default(0) @map("sort_order") + parent HostGroup? @relation("GroupTree", fields: [parentId], references: [id], onDelete: SetNull) + children HostGroup[] @relation("GroupTree") + hosts Host[] + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + @@map("host_groups") +} + +model Host { + id Int @id @default(autoincrement()) + name String + hostname String + port Int @default(22) + protocol Protocol @default(ssh) + groupId Int? @map("group_id") + credentialId Int? @map("credential_id") + tags String[] @default([]) + notes String? + color String? @db.VarChar(7) + sortOrder Int @default(0) @map("sort_order") + hostFingerprint String? @map("host_fingerprint") + lastConnectedAt DateTime? @map("last_connected_at") + group HostGroup? @relation(fields: [groupId], references: [id], onDelete: SetNull) + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + connectionLogs ConnectionLog[] + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + @@map("hosts") +} + +model Credential { + id Int @id @default(autoincrement()) + name String + username String? + domain String? + type CredentialType + encryptedValue String? @map("encrypted_value") + sshKeyId Int? @map("ssh_key_id") + sshKey SshKey? @relation(fields: [sshKeyId], references: [id], onDelete: SetNull) + hosts Host[] + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + @@map("credentials") +} + +model SshKey { + id Int @id @default(autoincrement()) + name String + keyType String @map("key_type") @db.VarChar(20) + fingerprint String? + publicKey String? @map("public_key") + encryptedPrivateKey String @map("encrypted_private_key") + passphraseEncrypted String? @map("passphrase_encrypted") + credentials Credential[] + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + @@map("ssh_keys") +} + +model ConnectionLog { + id Int @id @default(autoincrement()) + hostId Int @map("host_id") + protocol Protocol + connectedAt DateTime @default(now()) @map("connected_at") + disconnectedAt DateTime? @map("disconnected_at") + host Host @relation(fields: [hostId], references: [id], onDelete: Cascade) + + @@map("connection_logs") +} + +model Setting { + key String @id + value String + + @@map("settings") +} + +enum Protocol { + ssh + rdp +} + +enum CredentialType { + password + ssh_key +} diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts new file mode 100644 index 0000000..c146e78 --- /dev/null +++ b/backend/src/app.module.ts @@ -0,0 +1,23 @@ +import { Module } from '@nestjs/common'; +import { ServeStaticModule } from '@nestjs/serve-static'; +import { join } from 'path'; +import { PrismaModule } from './prisma/prisma.module'; +import { AuthModule } from './auth/auth.module'; +import { VaultModule } from './vault/vault.module'; +import { ConnectionsModule } from './connections/connections.module'; +import { SettingsModule } from './settings/settings.module'; + +@Module({ + imports: [ + PrismaModule, + AuthModule, + VaultModule, + ConnectionsModule, + SettingsModule, + ServeStaticModule.forRoot({ + rootPath: join(__dirname, '..', 'public'), + exclude: ['/api/(.*)'], + }), + ], +}) +export class AppModule {} diff --git a/backend/src/main.ts b/backend/src/main.ts new file mode 100644 index 0000000..46b038a --- /dev/null +++ b/backend/src/main.ts @@ -0,0 +1,18 @@ +import { NestFactory } from '@nestjs/core'; +import { ValidationPipe } from '@nestjs/common'; +import { WsAdapter } from '@nestjs/platform-ws'; +import { AppModule } from './app.module'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + app.setGlobalPrefix('api'); + app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true })); + app.useWebSocketAdapter(new WsAdapter(app)); + app.enableCors({ + origin: process.env.NODE_ENV === 'production' ? false : 'http://localhost:3001', + credentials: true, + }); + await app.listen(3000); + console.log('Wraith backend running on port 3000'); +} +bootstrap(); diff --git a/backend/src/prisma/prisma.module.ts b/backend/src/prisma/prisma.module.ts new file mode 100644 index 0000000..7207426 --- /dev/null +++ b/backend/src/prisma/prisma.module.ts @@ -0,0 +1,9 @@ +import { Global, Module } from '@nestjs/common'; +import { PrismaService } from './prisma.service'; + +@Global() +@Module({ + providers: [PrismaService], + exports: [PrismaService], +}) +export class PrismaModule {} diff --git a/backend/src/prisma/prisma.service.ts b/backend/src/prisma/prisma.service.ts new file mode 100644 index 0000000..bb6565f --- /dev/null +++ b/backend/src/prisma/prisma.service.ts @@ -0,0 +1,13 @@ +import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; + +@Injectable() +export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { + async onModuleInit() { + await this.$connect(); + } + + async onModuleDestroy() { + await this.$disconnect(); + } +}