feat: project scaffold — Docker, NestJS, Nuxt 3, Prisma config
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
99f3c5caab
commit
88dbb99f9d
3
.env.example
Normal file
3
.env.example
Normal file
@ -0,0 +1,3 @@
|
||||
DB_PASSWORD=changeme
|
||||
JWT_SECRET=generate-a-64-char-hex-string
|
||||
ENCRYPTION_KEY=generate-a-64-char-hex-string
|
||||
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.output/
|
||||
.nuxt/
|
||||
.env
|
||||
*.log
|
||||
.DS_Store
|
||||
backend/prisma/*.db
|
||||
27
Dockerfile
Normal file
27
Dockerfile
Normal file
@ -0,0 +1,27 @@
|
||||
# Stage 1: Frontend build
|
||||
FROM node:20-alpine AS frontend
|
||||
WORKDIR /app/frontend
|
||||
COPY frontend/package*.json ./
|
||||
RUN npm ci
|
||||
COPY frontend/ ./
|
||||
RUN npx nuxi generate
|
||||
|
||||
# Stage 2: Backend build
|
||||
FROM node:20-alpine AS backend
|
||||
WORKDIR /app/backend
|
||||
COPY backend/package*.json ./
|
||||
RUN npm ci
|
||||
COPY backend/ ./
|
||||
RUN npx prisma generate
|
||||
RUN npm run build
|
||||
|
||||
# Stage 3: Production
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
COPY --from=backend /app/backend/dist ./dist
|
||||
COPY --from=backend /app/backend/node_modules ./node_modules
|
||||
COPY --from=backend /app/backend/package.json ./
|
||||
COPY --from=backend /app/backend/prisma ./prisma
|
||||
COPY --from=frontend /app/frontend/.output/public ./public
|
||||
EXPOSE 3000
|
||||
CMD ["node", "dist/main.js"]
|
||||
29
README.md
Normal file
29
README.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Wraith
|
||||
|
||||
Self-hosted MobaXterm replacement — SSH + SFTP + RDP in a browser.
|
||||
|
||||
## Stack
|
||||
|
||||
- **Backend:** NestJS 10, Prisma 6, PostgreSQL 16, ssh2, guacd
|
||||
- **Frontend:** Nuxt 3 (SPA), PrimeVue 4, Tailwind CSS, xterm.js 5
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# Edit .env with real secrets
|
||||
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Default credentials: `admin@wraith.local` / `wraith`
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
cd backend && npm install && npm run dev
|
||||
|
||||
# Frontend
|
||||
cd frontend && npm install && npm run dev
|
||||
```
|
||||
8
backend/nest-cli.json
Normal file
8
backend/nest-cli.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"deleteOutDir": true
|
||||
}
|
||||
}
|
||||
63
backend/package.json
Normal file
63
backend/package.json
Normal file
@ -0,0 +1,63 @@
|
||||
{
|
||||
"name": "wraith-backend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nest build",
|
||||
"start": "node dist/main.js",
|
||||
"dev": "nest start --watch",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"prisma:migrate": "prisma migrate dev",
|
||||
"prisma:generate": "prisma generate",
|
||||
"prisma:seed": "ts-node prisma/seed.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^10.0.0",
|
||||
"@nestjs/core": "^10.0.0",
|
||||
"@nestjs/jwt": "^10.0.0",
|
||||
"@nestjs/mapped-types": "^2.0.0",
|
||||
"@nestjs/passport": "^10.0.0",
|
||||
"@nestjs/platform-express": "^10.0.0",
|
||||
"@nestjs/platform-ws": "^10.0.0",
|
||||
"@nestjs/serve-static": "^4.0.0",
|
||||
"@nestjs/websockets": "^10.0.0",
|
||||
"@prisma/client": "^6.0.0",
|
||||
"bcrypt": "^5.1.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"reflect-metadata": "^0.2.0",
|
||||
"rxjs": "^7.8.0",
|
||||
"ssh2": "^1.15.0",
|
||||
"ws": "^8.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^10.0.0",
|
||||
"@nestjs/testing": "^10.0.0",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/passport-jwt": "^4.0.0",
|
||||
"@types/ssh2": "^1.15.0",
|
||||
"@types/ws": "^8.5.0",
|
||||
"jest": "^29.0.0",
|
||||
"prisma": "^6.0.0",
|
||||
"ts-jest": "^29.0.0",
|
||||
"ts-node": "^10.9.0",
|
||||
"typescript": "^5.3.0"
|
||||
},
|
||||
"prisma": {
|
||||
"seed": "ts-node prisma/seed.ts"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": ["js", "json", "ts"],
|
||||
"rootDir": ".",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": { "^.+\\.ts$": "ts-jest" },
|
||||
"testEnvironment": "node",
|
||||
"moduleNameMapper": {
|
||||
"^@/(.*)$": "<rootDir>/src/$1"
|
||||
}
|
||||
}
|
||||
}
|
||||
4
backend/tsconfig.build.json
Normal file
4
backend/tsconfig.build.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "test", "dist", "**/*.spec.ts"]
|
||||
}
|
||||
22
backend/tsconfig.json
Normal file
22
backend/tsconfig.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": true,
|
||||
"noImplicitAny": true,
|
||||
"strictBindCallApply": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"paths": { "@/*": ["src/*"] }
|
||||
}
|
||||
}
|
||||
36
docker-compose.yml
Normal file
36
docker-compose.yml
Normal file
@ -0,0 +1,36 @@
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
ports: ["3000:3000"]
|
||||
environment:
|
||||
DATABASE_URL: postgresql://wraith:${DB_PASSWORD}@postgres:5432/wraith
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
|
||||
GUACD_HOST: guacd
|
||||
GUACD_PORT: "4822"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
guacd:
|
||||
condition: service_started
|
||||
restart: unless-stopped
|
||||
|
||||
guacd:
|
||||
image: guacamole/guacd
|
||||
restart: always
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
volumes: [pgdata:/var/lib/postgresql/data]
|
||||
environment:
|
||||
POSTGRES_DB: wraith
|
||||
POSTGRES_USER: wraith
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U wraith"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
5
frontend/app.vue
Normal file
5
frontend/app.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
7
frontend/assets/css/main.css
Normal file
7
frontend/assets/css/main.css
Normal file
@ -0,0 +1,7 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html, body, #__nuxt {
|
||||
@apply h-full bg-gray-900 text-gray-100;
|
||||
}
|
||||
5
frontend/layouts/auth.vue
Normal file
5
frontend/layouts/auth.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="min-h-screen flex items-center justify-center bg-gray-950">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
29
frontend/nuxt.config.ts
Normal file
29
frontend/nuxt.config.ts
Normal file
@ -0,0 +1,29 @@
|
||||
export default defineNuxtConfig({
|
||||
ssr: false,
|
||||
devtools: { enabled: false },
|
||||
modules: [
|
||||
'@pinia/nuxt',
|
||||
'@nuxtjs/tailwindcss',
|
||||
'@primevue/nuxt-module',
|
||||
],
|
||||
css: ['~/assets/css/main.css'],
|
||||
primevue: {
|
||||
options: {
|
||||
theme: 'none',
|
||||
},
|
||||
},
|
||||
runtimeConfig: {
|
||||
public: {
|
||||
apiBase: process.env.API_BASE || 'http://localhost:3000',
|
||||
},
|
||||
},
|
||||
devServer: {
|
||||
port: 3001,
|
||||
},
|
||||
nitro: {
|
||||
devProxy: {
|
||||
'/api': { target: 'http://localhost:3000/api', ws: true },
|
||||
'/ws': { target: 'ws://localhost:3000/ws', ws: true },
|
||||
},
|
||||
},
|
||||
})
|
||||
30
frontend/package.json
Normal file
30
frontend/package.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "wraith-frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "nuxi dev",
|
||||
"build": "nuxi generate",
|
||||
"preview": "nuxi preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pinia/nuxt": "^0.5.0",
|
||||
"@primevue/themes": "^4.0.0",
|
||||
"guacamole-common-js": "^1.5.0",
|
||||
"lucide-vue-next": "^0.300.0",
|
||||
"monaco-editor": "^0.45.0",
|
||||
"pinia": "^2.1.0",
|
||||
"primevue": "^4.0.0",
|
||||
"@xterm/xterm": "^5.4.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/addon-search": "^0.15.0",
|
||||
"@xterm/addon-web-links": "^0.11.0",
|
||||
"@xterm/addon-webgl": "^0.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxtjs/tailwindcss": "^6.0.0",
|
||||
"@primevue/nuxt-module": "^4.0.0",
|
||||
"nuxt": "^3.10.0",
|
||||
"typescript": "^5.3.0"
|
||||
}
|
||||
}
|
||||
32
frontend/tailwind.config.ts
Normal file
32
frontend/tailwind.config.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import type { Config } from 'tailwindcss'
|
||||
|
||||
export default {
|
||||
content: [
|
||||
'./components/**/*.vue',
|
||||
'./layouts/**/*.vue',
|
||||
'./pages/**/*.vue',
|
||||
'./composables/**/*.ts',
|
||||
'./app.vue',
|
||||
],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
wraith: {
|
||||
50: '#f0f4ff',
|
||||
100: '#dbe4ff',
|
||||
200: '#bac8ff',
|
||||
300: '#91a7ff',
|
||||
400: '#748ffc',
|
||||
500: '#5c7cfa',
|
||||
600: '#4c6ef5',
|
||||
700: '#4263eb',
|
||||
800: '#3b5bdb',
|
||||
900: '#364fc7',
|
||||
950: '#1e3a8a',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
} satisfies Config
|
||||
Loading…
Reference in New Issue
Block a user