Files
dashboard/DEVELOPMENT.md
Matthias Hochmeister f09748f4a1 inital
2026-02-23 17:08:58 +01:00

18 KiB

Development Guide

Welcome to the Feuerwehr Dashboard development guide. This document will help you get started with developing, testing, and contributing to the project.

Table of Contents

Developer Onboarding

Prerequisites

Before you begin, ensure you have:

  • Node.js 18+ - Download
  • Docker Desktop - Download
  • Git - Download
  • Code Editor - VS Code recommended
  • Authentik Access - For testing authentication
{
  "recommendations": [
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode",
    "ms-vscode.vscode-typescript-next",
    "bradlc.vscode-tailwindcss",
    "ms-azuretools.vscode-docker",
    "prisma.prisma"
  ]
}

First Steps

  1. Clone the repository:

    git clone <repository-url>
    cd feuerwehr_dashboard
    
  2. Read the documentation:

  3. Set up your environment (see below)

  4. Run the application locally

  5. Make a small change to familiarize yourself with the codebase

Local Development Setup

Start the development database:

make dev

This starts only PostgreSQL, allowing you to run backend/frontend locally with hot reload.

Option 2: Fully Dockerized (Quick Testing)

Run everything in Docker:

make prod

Note: This doesn't support hot reload, so it's less ideal for active development.

Option 3: Native Development (Full Control)

Step 1: Start Database

make dev

Or manually:

docker-compose -f docker-compose.dev.yml up -d

Step 2: Configure Backend

Create backend/.env:

cd backend
cat > .env << EOF
DATABASE_URL=postgresql://dev_user:dev_password@localhost:5432/feuerwehr_dev
JWT_SECRET=dev_secret_do_not_use_in_production
NODE_ENV=development
PORT=3000
LOG_LEVEL=debug

# Authentik - use your dev instance
AUTHENTIK_CLIENT_ID=your_dev_client_id
AUTHENTIK_CLIENT_SECRET=your_dev_client_secret
AUTHENTIK_ISSUER=http://localhost:9000/application/o/feuerwehr/
AUTHENTIK_REDIRECT_URI=http://localhost:5173/auth/callback
EOF

Step 3: Install Backend Dependencies

cd backend
npm install

Step 4: Run Backend

npm run dev

Backend will start on http://localhost:3000 with auto-reload on file changes.

Step 5: Configure Frontend

Create frontend/.env:

cd frontend
cat > .env << EOF
VITE_API_URL=http://localhost:3000
EOF

Step 6: Install Frontend Dependencies

cd frontend
npm install

Step 7: Run Frontend

npm run dev

Frontend will start on http://localhost:5173 with hot module replacement.

Verify Setup

Test backend:

curl http://localhost:3000/health
# Should return: {"status":"ok","timestamp":"..."}

Test frontend:

open http://localhost:5173
# Browser should open with login page

Project Structure

Backend Structure

backend/
├── src/
│   ├── config/              # Configuration files
│   │   ├── database.ts      # Database connection and pool
│   │   ├── environment.ts   # Environment variables
│   │   └── oauth.ts         # OAuth/Authentik configuration
│   ├── controllers/         # Request handlers
│   │   ├── auth.controller.ts
│   │   └── user.controller.ts
│   ├── database/
│   │   └── migrations/      # SQL migration files
│   │       ├── 001_create_users_table.sql
│   │       └── 002_create_sessions_table.sql
│   ├── middleware/          # Express middleware
│   │   ├── auth.middleware.ts      # Authentication
│   │   ├── error.middleware.ts     # Error handling
│   │   └── rate-limit.middleware.ts # Rate limiting
│   ├── models/              # Data models
│   │   └── user.model.ts
│   ├── routes/              # API routes
│   │   ├── auth.routes.ts
│   │   └── user.routes.ts
│   ├── services/            # Business logic
│   │   ├── auth.service.ts
│   │   ├── oauth.service.ts
│   │   ├── token.service.ts
│   │   └── user.service.ts
│   ├── types/               # TypeScript types
│   │   ├── auth.types.ts
│   │   └── user.types.ts
│   ├── utils/               # Utility functions
│   │   └── logger.ts
│   ├── app.ts               # Express app configuration
│   └── server.ts            # Server entry point
├── logs/                    # Application logs (gitignored)
├── Dockerfile
├── package.json
├── tsconfig.json
└── nodemon.json

Frontend Structure

frontend/
├── src/
│   ├── components/          # Reusable React components
│   │   ├── common/          # Shared components
│   │   │   ├── Header.tsx
│   │   │   └── Loading.tsx
│   │   └── layout/          # Layout components
│   │       └── MainLayout.tsx
│   ├── contexts/            # React Context providers
│   │   └── AuthContext.tsx  # Authentication state
│   ├── pages/               # Page components
│   │   ├── auth/
│   │   │   ├── LoginPage.tsx
│   │   │   └── CallbackPage.tsx
│   │   ├── dashboard/
│   │   │   └── DashboardPage.tsx
│   │   └── user/
│   │       └── ProfilePage.tsx
│   ├── services/            # API service layer
│   │   ├── api.ts           # Axios instance
│   │   ├── authService.ts   # Auth API calls
│   │   └── userService.ts   # User API calls
│   ├── theme/               # MUI theme configuration
│   │   └── theme.ts
│   ├── types/               # TypeScript types
│   │   └── auth.types.ts
│   ├── utils/               # Utility functions
│   │   └── storage.ts       # Local storage helpers
│   ├── App.tsx              # Main app component
│   ├── main.tsx             # Entry point
│   └── vite-env.d.ts        # Vite type declarations
├── public/                  # Static assets
├── nginx.conf               # Production Nginx config
├── Dockerfile
├── package.json
├── vite.config.ts
└── tsconfig.json

Code Architecture

Backend Architecture

Layered Architecture

  1. Routes Layer (routes/)

    • Define API endpoints
    • Input validation (Zod schemas)
    • Route to controllers
  2. Controllers Layer (controllers/)

    • Handle HTTP requests/responses
    • Call services for business logic
    • Format responses
  3. Services Layer (services/)

    • Business logic implementation
    • Database operations
    • External API calls (OAuth)
  4. Models Layer (models/)

    • Data structures
    • Database queries
    • Data validation
  5. Middleware Layer (middleware/)

    • Authentication
    • Error handling
    • Rate limiting
    • Logging

Request Flow

Client Request
    ↓
Express Router (routes/)
    ↓
Middleware (auth, validation)
    ↓
Controller (controllers/)
    ↓
Service (services/)
    ↓
Model (models/)
    ↓
Database
    ↓
← Response flows back up

Frontend Architecture

Component-Based Architecture

  1. Pages - Route-level components
  2. Layouts - Structural components (header, sidebar)
  3. Components - Reusable UI elements
  4. Contexts - Global state management
  5. Services - API communication

Data Flow

User Interaction
    ↓
Component Event Handler
    ↓
Service Layer (API call)
    ↓
Backend API
    ↓
Update Context/State
    ↓
Re-render Components

Authentication Flow

1. User clicks "Login"
   ↓
2. Frontend redirects to Authentik
   ↓
3. User authenticates with Authentik
   ↓
4. Authentik redirects to /auth/callback with code
   ↓
5. Frontend sends code to backend /api/auth/callback
   ↓
6. Backend exchanges code for tokens with Authentik
   ↓
7. Backend creates user session in database
   ↓
8. Backend returns JWT access/refresh tokens
   ↓
9. Frontend stores tokens and updates AuthContext
   ↓
10. User is redirected to dashboard

Development Workflow

1. Create Feature Branch

git checkout -b feature/your-feature-name
# or
git checkout -b fix/bug-description

2. Make Changes

  • Write code following style guidelines
  • Add comments for complex logic
  • Update types as needed

3. Test Locally

# Run backend tests
cd backend && npm test

# Run frontend tests
cd frontend && npm test

# Manual testing
# Test in browser at http://localhost:5173

4. Commit Changes

git add .
git commit -m "feat: add user profile editing feature"

Follow Conventional Commits:

  • feat: - New feature
  • fix: - Bug fix
  • docs: - Documentation
  • refactor: - Code refactoring
  • test: - Adding tests
  • chore: - Maintenance

5. Push and Create PR

git push origin feature/your-feature-name

Create pull request on your Git platform.

Adding New Features

Adding a New API Endpoint

Step 1: Define Types

Create or update backend/src/types/your-feature.types.ts:

export interface YourFeature {
  id: number;
  name: string;
  createdAt: Date;
}

export interface CreateYourFeatureDto {
  name: string;
}

Step 2: Create Model

Create backend/src/models/your-feature.model.ts:

import { pool } from '../config/database';
import { YourFeature } from '../types/your-feature.types';

export const createYourFeature = async (name: string): Promise<YourFeature> => {
  const result = await pool.query(
    'INSERT INTO your_features (name) VALUES ($1) RETURNING *',
    [name]
  );
  return result.rows[0];
};

Step 3: Create Service

Create backend/src/services/your-feature.service.ts:

import * as YourFeatureModel from '../models/your-feature.model';
import { CreateYourFeatureDto } from '../types/your-feature.types';

class YourFeatureService {
  async create(dto: CreateYourFeatureDto) {
    // Business logic here
    return await YourFeatureModel.createYourFeature(dto.name);
  }
}

export default new YourFeatureService();

Step 4: Create Controller

Create backend/src/controllers/your-feature.controller.ts:

import { Request, Response } from 'express';
import yourFeatureService from '../services/your-feature.service';

class YourFeatureController {
  async create(req: Request, res: Response) {
    try {
      const feature = await yourFeatureService.create(req.body);
      res.status(201).json(feature);
    } catch (error) {
      res.status(500).json({ error: 'Failed to create feature' });
    }
  }
}

export default new YourFeatureController();

Step 5: Create Routes

Create backend/src/routes/your-feature.routes.ts:

import { Router } from 'express';
import yourFeatureController from '../controllers/your-feature.controller';
import { authenticate } from '../middleware/auth.middleware';

const router = Router();

router.post('/', authenticate, yourFeatureController.create);

export default router;

Step 6: Register Routes

Update backend/src/app.ts:

import yourFeatureRoutes from './routes/your-feature.routes';

app.use('/api/your-features', yourFeatureRoutes);

Step 7: Create Database Migration

Create backend/src/database/migrations/00X_create_your_features_table.sql:

CREATE TABLE IF NOT EXISTS your_features (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_your_features_name ON your_features(name);

Adding a New Frontend Page

Step 1: Create Page Component

Create frontend/src/pages/your-feature/YourFeaturePage.tsx:

import React from 'react';
import { Container, Typography } from '@mui/material';

const YourFeaturePage: React.FC = () => {
  return (
    <Container>
      <Typography variant="h4">Your Feature</Typography>
    </Container>
  );
};

export default YourFeaturePage;

Step 2: Create Service

Create frontend/src/services/yourFeatureService.ts:

import api from './api';

export const createYourFeature = async (name: string) => {
  const response = await api.post('/api/your-features', { name });
  return response.data;
};

Step 3: Add Route

Update frontend/src/App.tsx:

import YourFeaturePage from './pages/your-feature/YourFeaturePage';

// In your routes
<Route path="/your-feature" element={<YourFeaturePage />} />

Testing Guidelines

Backend Testing

Create backend/src/__tests__/your-feature.test.ts:

import yourFeatureService from '../services/your-feature.service';

describe('YourFeatureService', () => {
  test('should create feature', async () => {
    const feature = await yourFeatureService.create({ name: 'Test' });
    expect(feature.name).toBe('Test');
  });
});

Run tests:

cd backend
npm test

Frontend Testing

Create frontend/src/__tests__/YourFeaturePage.test.tsx:

import { render, screen } from '@testing-library/react';
import YourFeaturePage from '../pages/your-feature/YourFeaturePage';

test('renders your feature page', () => {
  render(<YourFeaturePage />);
  expect(screen.getByText('Your Feature')).toBeInTheDocument();
});

Run tests:

cd frontend
npm test

Manual Testing Checklist

  • Feature works in development mode
  • Feature works in production build
  • Authenticated users can access
  • Unauthorized access is blocked
  • Error states are handled
  • Loading states are shown
  • Responsive on mobile
  • Works in Chrome, Firefox, Safari

Debugging

Backend Debugging

VS Code Launch Configuration

Create .vscode/launch.json:

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug Backend",
      "runtimeExecutable": "npm",
      "runtimeArgs": ["run", "dev"],
      "cwd": "${workspaceFolder}/backend",
      "console": "integratedTerminal"
    }
  ]
}

Console Logging

import logger from '../utils/logger';

logger.debug('Debug message', { data });
logger.info('Info message');
logger.warn('Warning message');
logger.error('Error message', { error });

Database Queries

Enable query logging in backend/src/config/database.ts:

const pool = new Pool({
  // ...config
  log: (msg) => logger.debug(msg)
});

Frontend Debugging

React DevTools

Install React Developer Tools browser extension.

Console Logging

console.log('Data:', data);
console.error('Error:', error);

Network Tab

Use browser DevTools Network tab to inspect API calls.

State Debugging

const [state, setState] = useState(initialState);

useEffect(() => {
  console.log('State changed:', state);
}, [state]);

Common Issues

Database Connection Failed

Problem: Backend can't connect to database

Solution:

# Verify database is running
docker ps | grep postgres

# Restart database
docker-compose -f docker-compose.dev.yml restart

# Check logs
docker logs feuerwehr_db_dev

Port Already in Use

Problem: Port 3000 or 5173 already occupied

Solution:

# Find process using port
lsof -i :3000

# Kill process
kill -9 <PID>

# Or change port in package.json

TypeScript Errors

Problem: Type errors in VS Code

Solution:

# Restart TypeScript server in VS Code
# Cmd+Shift+P > "TypeScript: Restart TS Server"

# Rebuild
cd backend && npm run build
cd frontend && npm run build

Authentik Redirect Fails

Problem: OAuth callback returns error

Solution:

  1. Verify redirect URI exactly matches in Authentik
  2. Check client ID and secret in .env
  3. Ensure Authentik is accessible from browser
  4. Check browser console for CORS errors

Hot Reload Not Working

Problem: Changes don't reflect immediately

Solution:

# Backend - check nodemon is running
# Frontend - restart Vite dev server
npm run dev

Best Practices

Code Style

  • Use TypeScript strict mode
  • Follow ESLint rules
  • Use Prettier for formatting
  • Write descriptive variable names
  • Add JSDoc comments for public APIs

Security

  • Never commit secrets to Git
  • Validate all inputs
  • Use parameterized queries
  • Sanitize user input
  • Keep dependencies updated

Performance

  • Use database indexes
  • Implement pagination for lists
  • Cache expensive operations
  • Optimize bundle size
  • Use React.memo for expensive components

Git Workflow

  • Keep commits small and focused
  • Write clear commit messages
  • Rebase before merging
  • Delete branches after merge
  • Don't commit node_modules/ or .env

Getting Help

  • Documentation: Check all .md files
  • Code Comments: Read inline documentation
  • Team Chat: Ask in development channel
  • GitHub Issues: Search existing issues
  • Stack Overflow: Search for similar problems

Next Steps

After completing setup:

  1. Explore the codebase
  2. Try adding a simple feature
  3. Read ARCHITECTURE.md
  4. Review API_DOCUMENTATION.md
  5. Check CONTRIBUTING.md

Happy coding!