PSA/docs/getting-started/development_guide.md
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

10 KiB

Development Guide

This guide covers development workflows, best practices, and common tasks when working with the PSA platform.

Development Environment Setup

Prerequisites

  • Docker Engine 24.0.0+
  • Docker Compose v2.20.0+
  • Node.js 18+
  • Git
  • VS Code (recommended)

Initial Setup

  1. Clone and setup:
git clone https://github.com/your-org/alga-psa.git
cd alga-psa
cp .env.example .env.development
  1. Create development secrets:
mkdir -p secrets
# Create development secrets with dummy values
echo 'dev-password' > secrets/db_password_server
echo 'dev-password' > secrets/db_password_hocuspocus
# ... create other required secrets
chmod 600 secrets/*
  1. Start development environment:
# For Community Edition
docker compose -f docker-compose.base.yaml -f docker-compose.ce.yaml up

# For Enterprise Edition
docker compose -f docker-compose.base.yaml -f docker-compose.ee.yaml up

Development Workflow

1. Code Organization

alga-psa/
├── server/                  # Next.js application server
│   ├── src/                 # Source code (App Router pages, components, lib)
│   └── migrations/          # Database migrations (CE)
├── packages/                # Shared @alga-psa/* packages (~50 packages)
│   ├── build-tools/         # Shared tsup build preset
│   └── <domain>/            # Domain packages (billing, clients, tickets, etc.)
├── ee/                      # Enterprise Edition (server, packages, migrations)
├── shared/                  # Legacy shared libraries
├── hocuspocus/              # Real-time collaboration server
├── services/
│   └── workflow-worker/     # Workflow processing service
└── sdk/                     # Extension SDK & samples

Package Build System

Domain logic lives in @alga-psa/* packages under packages/. Some are pre-built (compiled by tsup to dist/ before dev/build), others are source-transpiled (compiled by Next.js from src/).

  • npm run dev automatically builds all pre-built packages via npx nx build-deps server before starting the dev server
  • Nx caches build outputs — subsequent runs are near-instant
  • See Package Build System for full details on which packages use which mode and how to flip a package

2. Branch Strategy

  • main: Production-ready code
  • develop: Integration branch
  • feature/*: New features
  • fix/*: Bug fixes
  • release/*: Release preparation

3. Development Cycle

  1. Create feature branch:
git checkout -b feature/your-feature
  1. Start development environment:
docker compose -f docker-compose.base.yaml -f docker-compose.ce.yaml up
  1. Make changes and test
  2. Commit changes:
git add .
git commit -m "feat: description"
  1. Push and create PR:
git push origin feature/your-feature

Common Development Tasks

Database Migrations

  1. Create new migration:
cd server
npm run migrate:make your_migration_name
  1. Run migrations:
npm run migrate:latest
  1. Rollback:
npm run migrate:rollback

Testing

  1. Run all tests:
npm test
  1. Run specific tests:
npm test -- path/to/test
  1. Watch mode:
npm test -- --watch

Working with Docker

  1. Rebuild specific service:
docker compose build server
  1. View logs:
docker compose logs -f [service]
  1. Restart service:
docker compose restart [service]
  1. Clean up:
docker compose down -v

Development Best Practices

1. Code Style

  • Follow ESLint configuration
  • Use TypeScript for type safety
  • Follow existing patterns
  • Document complex logic
  • Write meaningful commit messages

2. Server Action Authentication

All server actions that need authentication and database access should use the withAuth wrapper:

import { withAuth, hasPermission } from '@alga-psa/auth';
import { createTenantKnex } from '@alga-psa/db';

export const myAction = withAuth(async (user, { tenant }, arg1: string): Promise<Result> => {
  const { knex } = await createTenantKnex();

  if (!await hasPermission(user, 'resource', 'action')) {
    throw new Error('Permission denied');
  }

  return knex('table').where({ tenant }).select('*');
});

Key points:

  • withAuth handles authentication and sets tenant context via AsyncLocalStorage
  • The wrapper provides typed user (IUserWithRoles) and { tenant } as first two arguments
  • Additional action arguments follow after the context
  • Always check permissions using hasPermission(user, resource, action)

3. Testing

  • Write tests for new features
  • Maintain test coverage
  • Use meaningful test descriptions
  • Test edge cases
  • Mock external dependencies

4. Docker

  • Keep images minimal
  • Use multi-stage builds
  • Don't store secrets in images
  • Use proper cache busting
  • Tag images appropriately

5. Security

  • Never commit secrets
  • Use environment variables
  • Validate user input
  • Follow OWASP guidelines
  • Regular dependency updates

Debugging

1. Server Debugging

  1. Enable debug logs:
docker compose up -f docker-compose.base.yaml -f docker-compose.ce.yaml -e DEBUG=true
  1. Use VS Code debugger:
    • Launch configuration provided
    • Breakpoints supported
    • Variable inspection
    • Call stack tracking

2. Database Debugging

  1. Connect to database:
docker compose exec postgres psql -U psa_user psa_db
  1. View logs:
docker compose logs postgres

3. Event Bus and Redis Debugging

  1. Redis CLI:
docker compose exec redis redis-cli
  1. Monitor all Redis events:
docker compose exec redis redis-cli monitor
  1. Monitor event streams:
# Monitor all events
docker compose exec redis redis-cli psubscribe "alga-psa:event:*"

# Monitor specific event type
docker compose exec redis redis-cli psubscribe "alga-psa:event:TICKET_UPDATED"
  1. View event bus subscribers:
docker compose exec redis redis-cli pubsub channels "alga-psa:event:*"
  1. Debug event bus configuration:
# Check Redis connection
docker compose exec redis redis-cli ping

# View event bus channels
docker compose exec redis redis-cli pubsub channels

# Check channel subscribers
docker compose exec redis redis-cli pubsub numsub channel_name

Event Bus System

1. Configuration

The event bus system uses Redis for event streaming. Configure through environment variables:

# Redis Configuration
REDIS_URL=redis://localhost:6379
REDIS_PREFIX=alga-psa:
REDIS_EVENT_PREFIX=event:
REDIS_RECONNECT_RETRIES=10
REDIS_RECONNECT_INITIAL_DELAY=100
REDIS_RECONNECT_MAX_DELAY=3000

2. Working with Events

  1. Create new event types:
// In server/src/lib/eventBus/events.ts
export const EventTypeEnum = z.enum([
  'YOUR_NEW_EVENT',
  // ... other events
]);

export const YourEventPayloadSchema = BasePayloadSchema.extend({
  // Define your event payload schema
  // BasePayloadSchema already includes tenantId
});

// Add to EventPayloadSchemas
export const EventPayloadSchemas = {
  YOUR_NEW_EVENT: YourEventPayloadSchema,
  // ... other schemas
};
  1. Create event subscriber:
// In server/src/lib/eventBus/subscribers/yourSubscriber.ts
import { eventBus } from '../index';
import { YourEvent, EventType } from '../events';

async function handleYourEvent(event: YourEvent): Promise<void> {
  const { tenantId } = event.payload;
  // Handle the event
}

export async function registerYourSubscriber(): Promise<void> {
  await eventBus.subscribe(
    'YOUR_NEW_EVENT',
    handleYourEvent
  );
}
  1. Publish events:
import { eventBus } from 'lib/eventBus';

await eventBus.publish({
  eventType: 'YOUR_NEW_EVENT',
  payload: {
    tenantId: 'tenant-id',
    // Your event data
  },
});

3. Testing Events

  1. Create event bus mocks:
// In your test file
jest.mock('lib/eventBus', () => ({
  eventBus: {
    publish: jest.fn(),
    subscribe: jest.fn(),
  },
}));
  1. Test event publishing:
test('should publish event', async () => {
  const event = {
    eventType: 'YOUR_NEW_EVENT',
    payload: {
      tenantId: 'test-tenant',
      // ... other payload data
    },
  };
  
  await yourFunction();
  
  expect(eventBus.publish).toHaveBeenCalledWith(
    expect.objectContaining(event)
  );
});
  1. Test event handling:
test('should handle event', async () => {
  const event = {
    id: 'test-id',
    eventType: 'YOUR_NEW_EVENT',
    timestamp: new Date().toISOString(),
    payload: {
      tenantId: 'test-tenant',
      // ... other payload data
    },
  };
  
  await handleYourEvent(event);
  
  // Assert expected behavior
});

Performance Optimization

1. Database

  • Index frequently queried fields
  • Optimize complex queries
  • Regular VACUUM
  • Monitor query performance

2. Application

  • Use caching effectively
  • Optimize API responses
  • Implement pagination
  • Profile memory usage

3. Docker

  • Optimize image sizes
  • Use volume mounts
  • Configure resource limits
  • Monitor container stats

Troubleshooting

Common Issues

  1. Database Connection Issues

    • Check credentials
    • Verify host/port
    • Check network connectivity
  2. Redis Connection Issues

    • Verify password
    • Check persistence config
    • Monitor memory usage
  3. Build Issues

    • Clear Docker cache
    • Update dependencies
    • Check Dockerfile syntax

Debug Commands

# Check service status
docker compose ps

# View service logs
docker compose logs [service]

# Check network
docker network inspect alga-psa_default

# Container shell access
docker compose exec [service] sh

Development Tools

  • Docker
  • ESLint
  • Prettier
  • TypeScript
  • GitLens
  • REST Client

Useful Scripts

  1. Development setup:
./scripts/dev-setup.sh
  1. Test data generation:
./scripts/generate-test-data.sh
  1. Dependency updates:
./scripts/update-deps.sh

Additional Resources