Bluewoo HRMS
AI Development GuideBootstrap

Bootstrap Artifacts

Copy-paste ready files to bootstrap the HRMS project

Bootstrap Artifacts

These are the essential files needed to bootstrap the HRMS project. Copy these exactly to get started.

Environment Variables

Create .env.example in the project root:

# Database
DATABASE_URL="postgresql://postgres:password@localhost:5432/hrms_dev?schema=public"

# Auth.js
AUTH_SECRET="your-auth-secret-here-generate-with-openssl-rand-base64-32"
AUTH_URL="http://localhost:3000"

# AI Service
AI_SERVICE_URL="http://localhost:3001"
OPENAI_API_KEY="sk-your-openai-api-key"

# MongoDB (for AI service)
MONGODB_URI="mongodb://localhost:27017/hrms_ai"

# Google Cloud Storage
GCS_BUCKET_NAME="hrms-documents"
GCS_PROJECT_ID="your-gcp-project-id"
GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"

# Redis (future enhancement - not required for MVP)
# REDIS_URL="redis://localhost:6379"

# Application
NODE_ENV="development"
PORT=3000
API_PREFIX="/api/v1"

# JWT
JWT_SECRET="your-jwt-secret-here"
JWT_EXPIRES_IN="1h"
JWT_REFRESH_EXPIRES_IN="7d"

Docker Compose

Create docker-compose.yml for local development:

version: '3.8'

services:
  postgres:
    image: postgres:17-alpine
    container_name: hrms_postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: hrms_dev
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

  mongodb:
    image: mongo:7
    container_name: hrms_mongodb
    ports:
      - "27017:27017"
    volumes:
      - mongodb_data:/data/db
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 5s
      timeout: 5s
      retries: 5

  # Redis - optional for caching (Phase 10+)
  # redis:
  #   image: redis:7-alpine
  #   container_name: hrms_redis
  #   ports:
  #     - "6379:6379"
  #   volumes:
  #     - redis_data:/data
  #   healthcheck:
  #     test: ["CMD", "redis-cli", "ping"]
  #     interval: 5s
  #     timeout: 5s
  #     retries: 5

volumes:
  postgres_data:
  mongodb_data:
  # redis_data:

Environment Validation

Create src/config/env.schema.ts:

import { z } from 'zod';

export const envSchema = z.object({
  // Database
  DATABASE_URL: z.string().url(),

  // Auth
  AUTH_SECRET: z.string().min(32),
  AUTH_URL: z.string().url(),

  // AI Service
  AI_SERVICE_URL: z.string().url(),
  OPENAI_API_KEY: z.string().startsWith('sk-'),

  // MongoDB
  MONGODB_URI: z.string().startsWith('mongodb'),

  // GCS (optional in dev)
  GCS_BUCKET_NAME: z.string().optional(),
  GCS_PROJECT_ID: z.string().optional(),
  GOOGLE_APPLICATION_CREDENTIALS: z.string().optional(),

  // Redis (optional - future enhancement)
  REDIS_URL: z.string().url().optional(),

  // Application
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
  PORT: z.coerce.number().default(3000),
  API_PREFIX: z.string().default('/api/v1'),

  // JWT
  JWT_SECRET: z.string().min(32),
  JWT_EXPIRES_IN: z.string().default('1h'),
  JWT_REFRESH_EXPIRES_IN: z.string().default('7d'),
});

export type EnvConfig = z.infer<typeof envSchema>;

export function validateEnv(): EnvConfig {
  const result = envSchema.safeParse(process.env);

  if (!result.success) {
    console.error('Invalid environment variables:');
    console.error(result.error.format());
    process.exit(1);
  }

  return result.data;
}

Project Folder Structure

hrms/
├── apps/
│   ├── api/                     # NestJS Backend
│   │   ├── src/
│   │   │   ├── config/          # Configuration (env validation)
│   │   │   ├── common/          # Shared utilities, guards, decorators
│   │   │   │   ├── guards/      # Auth, Permission, Tenant guards
│   │   │   │   ├── decorators/  # Custom decorators (@TenantId, @CurrentUser)
│   │   │   │   ├── filters/     # Exception filters
│   │   │   │   ├── interceptors/# Response interceptors
│   │   │   │   └── dto/         # Base DTOs
│   │   │   ├── modules/         # Feature modules
│   │   │   │   ├── auth/        # Authentication module
│   │   │   │   ├── tenant/      # Tenant module
│   │   │   │   ├── employee/    # Employee module
│   │   │   │   ├── department/  # Department module
│   │   │   │   ├── team/        # Team module
│   │   │   │   ├── time-off/    # Time-off module
│   │   │   │   ├── document/    # Document module
│   │   │   │   ├── dashboard/   # Dashboard module
│   │   │   │   ├── goal/        # Goal module
│   │   │   │   └── feed/        # Team feed module
│   │   │   ├── prisma/          # Prisma service and module
│   │   │   ├── app.module.ts
│   │   │   └── main.ts
│   │   ├── prisma/
│   │   │   ├── schema.prisma
│   │   │   ├── migrations/
│   │   │   └── seed.ts
│   │   ├── test/
│   │   └── package.json
│   │
│   └── web/                     # Next.js Frontend
│   │   ├── src/
│   │   │   ├── app/             # App Router pages
│   │   │   │   ├── (auth)/      # Auth pages (login, register)
│   │   │   │   ├── (dashboard)/ # Dashboard pages
│   │   │   │   └── api/         # API routes
│   │   │   ├── components/      # React components
│   │   │   │   ├── ui/          # Base UI components
│   │   │   │   ├── forms/       # Form components
│   │   │   │   └── features/    # Feature-specific components
│   │   │   ├── hooks/           # Custom hooks
│   │   │   ├── lib/             # Utilities
│   │   │   ├── providers/       # Context providers
│   │   │   └── types/           # TypeScript types
│   │   └── package.json
│   │

├── packages/
│   ├── database/                # Shared Prisma client
│   │   ├── prisma/
│   │   │   └── schema.prisma
│   │   └── package.json
│   │
│   └── hrms-ai/                 # Express AI Service
│       ├── src/
│       │   ├── config/
│       │   ├── routes/
│       │   ├── services/
│       │   │   ├── embedding.service.ts
│       │   │   ├── rag.service.ts
│       │   │   └── chat.service.ts
│       │   ├── db/              # MongoDB connection
│       │   └── index.ts
│       └── package.json

├── docker-compose.yml
├── .env.example
├── .cursorrules               # Cursor AI rules
├── CLAUDE.md                  # Claude Code rules
└── package.json               # Workspace root

Package.json (Workspace Root)

{
  "name": "hrms",
  "private": true,
  "workspaces": [
    "apps/*",
    "packages/*"
  ],
  "scripts": {
    "dev": "npm run dev --workspaces --if-present",
    "dev:api": "npm run dev -w api",
    "dev:web": "npm run dev -w web",
    "dev:ai": "npm run dev -w hrms-ai",
    "build": "npm run build --workspaces --if-present",
    "test": "npm test --workspaces --if-present",
    "lint": "npm run lint --workspaces --if-present",
    "db:generate": "npm run prisma generate -w database",
    "db:migrate": "npm run prisma migrate dev -w database",
    "db:seed": "npm run prisma db seed -w database",
    "db:studio": "npm run prisma studio -w database"
  },
  "devDependencies": {
    "typescript": "^5.7.3"
  },
  "engines": {
    "node": ">=20.0.0"
  }
}

Prisma Seed Script

Create packages/database/prisma/seed.ts:

import { PrismaClient, SystemRole } from '@prisma/client';
import * as bcrypt from 'bcrypt';

const prisma = new PrismaClient();

async function main() {
  console.log('Seeding database...');

  // Create default tenant
  const tenant = await prisma.tenant.upsert({
    where: { domain: 'demo.hrms.local' },
    update: {},
    create: {
      name: 'Demo Company',
      domain: 'demo.hrms.local',
      status: 'ACTIVE',
      settings: {
        timezone: 'America/New_York',
        dateFormat: 'MM/DD/YYYY',
        currency: 'USD',
      },
    },
  });

  console.log(`Created tenant: ${tenant.name}`);

  // Create system admin user
  const adminPasswordHash = await bcrypt.hash('Admin123!', 10);
  const admin = await prisma.user.upsert({
    where: {
      tenantId_email: {
        tenantId: tenant.id,
        email: 'admin@demo.hrms.local',
      },
    },
    update: {},
    create: {
      tenantId: tenant.id,
      email: 'admin@demo.hrms.local',
      name: 'System Admin',
      systemRole: 'SYSTEM_ADMIN',
      status: 'ACTIVE',
    },
  });

  console.log(`Created admin user: ${admin.email}`);

  // Create demo employee
  const employee = await prisma.employee.upsert({
    where: {
      tenantId_email: {
        tenantId: tenant.id,
        email: 'john.doe@demo.hrms.local',
      },
    },
    update: {},
    create: {
      tenantId: tenant.id,
      employeeNumber: 'EMP001',
      firstName: 'John',
      lastName: 'Doe',
      email: 'john.doe@demo.hrms.local',
      jobTitle: 'Software Engineer',
      employmentType: 'FULL_TIME',
      workMode: 'HYBRID',
      status: 'ACTIVE',
      hireDate: new Date('2024-01-15'),
    },
  });

  console.log(`Created employee: ${employee.firstName} ${employee.lastName}`);

  // Create demo department
  const engineering = await prisma.department.upsert({
    where: {
      tenantId_code: {
        tenantId: tenant.id,
        code: 'ENG',
      },
    },
    update: {},
    create: {
      tenantId: tenant.id,
      name: 'Engineering',
      code: 'ENG',
      description: 'Engineering department',
      status: 'ACTIVE',
    },
  });

  console.log(`Created department: ${engineering.name}`);

  // Create demo team
  const backendTeam = await prisma.team.upsert({
    where: { id: 'backend-team-seed' },
    update: {},
    create: {
      id: 'backend-team-seed',
      tenantId: tenant.id,
      name: 'Backend Team',
      description: 'Backend development team',
      type: 'PERMANENT',
      status: 'ACTIVE',
    },
  });

  console.log(`Created team: ${backendTeam.name}`);

  // Create time-off policy
  const vacationPolicy = await prisma.timeOffPolicy.upsert({
    where: {
      tenantId_code: {
        tenantId: tenant.id,
        code: 'VACATION',
      },
    },
    update: {},
    create: {
      tenantId: tenant.id,
      name: 'Vacation',
      code: 'VACATION',
      description: 'Annual vacation leave',
      leaveType: 'VACATION',
      accrualType: 'ANNUAL',
      annualAllowance: 20,
      carryOverLimit: 5,
      requiresApproval: true,
      status: 'ACTIVE',
    },
  });

  console.log(`Created time-off policy: ${vacationPolicy.name}`);

  console.log('Seeding completed!');
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

Health Check Endpoint

Add to apps/api/src/app.controller.ts:

import { Controller, Get } from '@nestjs/common';

@Controller()
export class AppController {
  @Get('health')
  healthCheck() {
    return {
      status: 'ok',
      timestamp: new Date().toISOString(),
      uptime: process.uptime(),
    };
  }

  @Get('api/health')
  apiHealthCheck() {
    return {
      status: 'ok',
      timestamp: new Date().toISOString(),
      version: '1.0.0',
    };
  }
}

Quick Start Commands

# 1. Clone and install
git clone <repo-url>
cd hrms
npm install

# 2. Start infrastructure
docker compose up -d

# 3. Setup database
npm run db:generate
npm run db:migrate
npm run db:seed

# 4. Start development servers
npm run dev

# Backend: http://localhost:3000
# Frontend: http://localhost:3001
# AI Service: http://localhost:3002

Next Steps

After bootstrapping, proceed to:

  1. Phase 00: Project Bootstrap
  2. Reference: Tenant Implementation