Skip to content

Development Guide

shishkabob27 edited this page Aug 7, 2025 · 2 revisions

Development Guide

This guide covers everything you need to know to contribute to Flowcase development, including setup, architecture, coding standards, and contribution workflow.

🚀 Getting Started

Prerequisites

Before you begin developing with Flowcase, ensure you have:

  • Git: Version control system
  • Docker: 20.0+ with Docker Compose
  • Code Editor: VS Code, PyCharm, or your preferred editor
  • Basic Knowledge: Flask, Docker, SQLAlchemy, JavaScript/HTML/CSS

[!INFO] Development Approach: Flowcase development primarily uses Docker containers.

Development Environment Setup

1. Fork and Clone Repository

# Fork the repository on GitHub first, then:
git clone https://github.com/your-username/flowcase.git
cd flowcase

# Add upstream remote
git remote add upstream https://github.com/flowcase/flowcase.git

2. Development Container Setup

# Start development environment
docker compose -f docker-compose.dev.yml up -d

# View logs
docker compose -f docker-compose.dev.yml logs -f

🏗️ Project Structure

Directory Overview

flowcase/
├── __init__.py              # Flask application factory
├── run.py                   # Application entry point
├── config/
│   └── config.py           # Configuration management
├── models/                  # Database models
│   ├── __init__.py
│   ├── user.py            # User and Group models
│   ├── droplet.py         # Droplet and Instance models
│   ├── log.py             # Logging model
│   └── registry.py        # Registry model
├── routes/                  # Flask blueprints/routes
│   ├── __init__.py
│   ├── auth.py           # Authentication routes
│   ├── admin.py          # Admin panel routes
│   └── droplet.py        # Droplet management routes
├── utils/                   # Utility functions
│   ├── __init__.py
│   ├── docker.py         # Docker integration
│   ├── logger.py         # Logging utilities
│   ├── permissions.py    # Permission checking
│   └── setup.py          # Initial setup
├── templates/              # Jinja2 templates
│   ├── 404.html
│   ├── dashboard.html
│   ├── droplet.html
│   └── login.html
├── static/                 # Static assets
│   ├── css/              # Stylesheets
│   ├── js/               # JavaScript files
│   └── img/              # Images
├── docker-compose.yml      # Production compose
├── docker-compose.dev.yml  # Development compose
├── web.Dockerfile         # Web container definition
├── gunicorn.conf.py       # WSGI server config
└── requirements.txt       # Python dependencies

Key Components

Flask Application (__init__.py)

  • Application factory pattern
  • Blueprint registration
  • Database initialization
  • Error handling

Models (models/)

  • SQLAlchemy ORM models
  • Database schema definitions
  • Model relationships

Routes (routes/)

  • Flask blueprints for different features
  • API endpoints
  • Request handling logic

Utilities (utils/)

  • Docker integration
  • Permission management
  • System utilities

🔧 Development Workflow

Making Changes

1. Create Feature Branch

# Update main branch
git checkout main
git pull upstream main

# Create feature branch
git checkout -b feature/your-feature-name

2. Development Process

  1. Make changes to the codebase
  2. Test locally using development environment
  3. Write/update tests (when test framework is available)
  4. Update documentation if needed
  5. Commit changes with clear messages

Important

Testing Requirement: Until automated testing is implemented, thorough manual testing is essential. Test all affected functionality and edge cases before submitting pull requests.

3. Testing Changes

# Restart development environment
docker compose -f docker-compose.dev.yml restart

# Check logs for errors
docker compose -f docker-compose.dev.yml logs -f web

# Test manually through web interface
# Access http://localhost:5000

Tip

Manual Testing Checklist: Test authentication, admin panel functionality, droplet operations, and error handling scenarios. Document your testing process in pull request descriptions.

🧪 Testing

Manual Testing

Until automated testing is implemented, follow these manual testing procedures:

Basic Functionality Tests

  1. Authentication:

    • Login with admin/user accounts
    • Logout functionality
    • Session persistence
  2. Admin Panel:

    • User management (create, edit, delete)
    • Group management
    • Droplet management
    • System information display
  3. Droplet Operations:

    • Instance creation
    • Connection to instances
    • Instance destruction
    • File upload/download
  4. Error Handling:

    • Invalid credentials
    • Non-existent resources
    • Network failures

Browser Testing

Test across different browsers:

  • Chrome/Chromium
  • Firefox
  • Safari
  • Edge

Automated Testing (Future)

Planned testing framework:

# tests/test_auth.py (example)
import unittest
from __init__ import create_app, db

class AuthTestCase(unittest.TestCase):
    def setUp(self):
        self.app = create_app({'TESTING': True})
        self.client = self.app.test_client()
        
    def test_login(self):
        # Test login functionality
        pass
        
    def test_logout(self):
        # Test logout functionality
        pass

Warning

Testing Status: Automated testing framework is not yet implemented. Currently, all testing is manual. Setting up automated testing is a high-priority development goal for improving code quality and reliability.

[!HELP_WANTED] Contribution Opportunity: Implementing automated testing (unit tests, integration tests, end-to-end tests) is a great way to contribute to the project and would be highly valuable.

📝 Coding Standards

Python Code Style

Follow PEP 8 with these specific guidelines:

Import Organization

# Standard library imports
import os
import sys
import uuid

# Third-party imports
from flask import Flask, request, jsonify
from sqlalchemy import Column, String, DateTime

# Local imports
from __init__ import db
from models.user import User
from utils.permissions import Permissions

Function and Class Naming

# Functions: snake_case
def create_user(username, password):
    pass

# Classes: PascalCase
class DropletInstance:
    pass

# Constants: UPPER_SNAKE_CASE
DEFAULT_MEMORY_LIMIT = 1024

Documentation

def create_droplet_instance(droplet_id, user_id, resolution=None):
    """Create a new droplet instance for a user.
    
    Args:
        droplet_id (str): UUID of the droplet to instantiate
        user_id (str): UUID of the user creating the instance
        resolution (str, optional): Screen resolution (e.g., "1920x1080")
        
    Returns:
        DropletInstance: The created instance object
        
    Raises:
        ValueError: If droplet or user doesn't exist
        ResourceError: If insufficient resources available
    """
    pass

JavaScript Code Style

Modern JavaScript Practices

// Use const/let instead of var
const apiUrl = '/api/droplets';
let currentUser = null;

// Arrow functions for callbacks
droplets.forEach(droplet => {
    console.log(droplet.display_name);
});

// Async/await for promises
async function getDroplets() {
    try {
        const response = await fetch('/api/droplets');
        return await response.json();
    } catch (error) {
        console.error('Failed to fetch droplets:', error);
    }
}

Error Handling

// Always handle errors gracefully
function createInstance(dropletId) {
    fetch('/api/instance/request', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({droplet_id: dropletId})
    })
    .then(response => {
        if (!response.ok) {
            throw new Error('Failed to create instance');
        }
        return response.json();
    })
    .then(data => {
        if (data.success) {
            window.location.href = data.connect_url;
        } else {
            showNotification(data.error, 'error');
        }
    })
    .catch(error => {
        console.error('Error:', error);
        showNotification('Network error occurred', 'error');
    });
}

HTML/CSS Standards

Semantic HTML

<!-- Use semantic HTML elements -->
<nav class="navigation">
    <h1>Flowcase</h1>
    <div class="nav-links">
        <a href="#" class="user-link">
            <i class="fas fa-user"></i> Username
        </a>
    </div>
</nav>

<main class="content">
    <section class="droplets">
        <article class="droplet">
            <h2>Droplet Name</h2>
            <p>Description</p>
        </article>
    </section>
</main>

CSS Organization

/* Use consistent naming and organization */
.nav-links {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.nav-links a {
    color: var(--text-color);
    text-decoration: none;
    transition: color 0.2s ease;
}

.nav-links a:hover {
    color: var(--accent-color);
}

🔄 Database Development

Model Development

Creating New Models

# models/new_model.py
import uuid
from sqlalchemy.sql import func
from __init__ import db

class NewModel(db.Model):
    id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
    name = db.Column(db.String(80), nullable=False)
    created_at = db.Column(db.DateTime, server_default=func.now())
    
    def to_dict(self):
        """Convert model to dictionary for JSON serialization."""
        return {
            'id': self.id,
            'name': self.name,
            'created_at': self.created_at.isoformat() if self.created_at else None
        }

Database Migrations

# For manual migration during development
from __init__ import create_app, db

app = create_app()
with app.app_context():
    # Create new tables
    db.create_all()
    
    # Or drop and recreate (DEVELOPMENT ONLY)
    db.drop_all()
    db.create_all()

API Development

REST Endpoint Pattern

# routes/new_feature.py
from flask import Blueprint, request, jsonify
from flask_login import login_required, current_user
from models.new_model import NewModel
from __init__ import db

new_bp = Blueprint('new_feature', __name__)

@new_bp.route('/api/new-resource', methods=['GET'])
@login_required
def get_resources():
    """Get all resources for current user."""
    try:
        resources = NewModel.query.all()
        return jsonify({
            'success': True,
            'resources': [r.to_dict() for r in resources]
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@new_bp.route('/api/new-resource', methods=['POST'])
@login_required
def create_resource():
    """Create a new resource."""
    try:
        data = request.get_json()
        
        # Validate input
        if not data or 'name' not in data:
            return jsonify({
                'success': False,
                'error': 'Name is required'
            }), 400
            
        # Create resource
        resource = NewModel(name=data['name'])
        db.session.add(resource)
        db.session.commit()
        
        return jsonify({
            'success': True,
            'resource': resource.to_dict()
        }), 201
        
    except Exception as e:
        db.session.rollback()
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

🐳 Docker Development

Custom Container Images

Creating Droplet Images

# examples/custom-droplet.Dockerfile
FROM ubuntu:20.04

# Install desktop environment and VNC
RUN apt-get update && apt-get install -y \
    ubuntu-desktop-minimal \
    tigervnc-standalone-server \
    tigervnc-xorg-extension \
    supervisor \
    && rm -rf /var/lib/apt/lists/*

# Install your custom application
RUN apt-get update && apt-get install -y \
    your-custom-app \
    && rm -rf /var/lib/apt/lists/*

# Configure VNC and supervisor
COPY supervisord.conf /etc/supervisor/conf.d/
COPY startup.sh /usr/local/bin/

EXPOSE 5901
CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"]

Testing Custom Images

# Build custom image
docker build -f custom-droplet.Dockerfile -t my-custom-droplet .

# Test locally
docker run -d -p 5901:5901 my-custom-droplet

# Add to Flowcase as droplet
# Use image name: my-custom-droplet

🔍 Debugging

Backend Debugging

Flask Debug Mode

# Enable debug mode in development
app.config['DEBUG'] = True

# Add debug prints
import logging
logging.basicConfig(level=logging.DEBUG)

def debug_function():
    logging.debug(f"Variable value: {some_variable}")

Database Debugging

# Enable SQL query logging
app.config['SQLALCHEMY_ECHO'] = True

# Manual database inspection
from __init__ import create_app, db
app = create_app()
with app.app_context():
    result = db.engine.execute("SELECT * FROM user")
    for row in result:
        print(row)

Frontend Debugging

Browser Developer Tools

// Add debug logging
console.log('Debug info:', data);
console.error('Error occurred:', error);

// Inspect variables
debugger; // Breakpoint for browser debugger

// Network debugging
fetch('/api/endpoint')
    .then(response => {
        console.log('Response status:', response.status);
        console.log('Response headers:', response.headers);
        return response.json();
    });

Container Debugging

Accessing Running Containers

# List all containers
docker ps -a

# Access container shell
docker exec -it flowcase-web bash
docker exec -it flowcase_generated_xxx bash

# View container logs
docker logs flowcase-web
docker logs -f flowcase_generated_xxx

# Inspect container configuration
docker inspect flowcase-web

📦 Building and Deployment

Development Build

# Build development image
docker build -f web.Dockerfile -t flowcase:dev .

# Test with development compose
docker compose -f docker-compose.dev.yml up --build

Production Preparation

# Build production image
docker build -f web.Dockerfile -t flowcase:latest .

# Test production configuration
docker compose up --build

# Verify functionality
curl -I http://localhost:80

Warning

Production Testing: Since automated testing is not yet available, production builds require extensive manual testing. Test all critical functionality before release.

🤝 Contributing Guidelines

Commit Message Format

type(scope): short description

Longer description explaining what and why vs. how.
Can include multiple paragraphs.

Fixes #123
Refs #456

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation changes
  • style: Code style changes (formatting)
  • refactor: Code refactoring
  • test: Adding/updating tests
  • chore: Maintenance tasks

[!INFO] Testing Commits: Use the test type when contributing to the testing framework setup or adding test cases for future implementation.

Pull Request Process

  1. Create feature branch from main
  2. Make your changes following coding standards
  3. Test thoroughly using development environment
  4. Update documentation if needed
  5. Commit with clear messages
  6. Push to your fork
  7. Create pull request with:
    • Clear description of changes
    • Screenshots for UI changes
    • Testing instructions
    • Links to relevant issues

Code Review Guidelines

For Contributors

  • Keep PRs focused and reasonably sized
  • Include tests when possible
  • Document complex logic
  • Respond promptly to review feedback

For Reviewers

  • Check functionality and code quality
  • Verify security implications
  • Test manually when needed
  • Provide constructive feedback

🚀 Release Process

Version Management

# Update version in __init__.py
__version__ = "1.2.0"

# Tag release
git tag -a v1.2.0 -m "Release version 1.2.0"
git push upstream v1.2.0

Release Checklist

  • Update version number
  • Update CHANGELOG.md
  • Test installation from scratch
  • Verify all documentation is current
  • Create release notes
  • Tag and push release

📚 Resources

Learning Resources

Development Tools

  • IDEs: VS Code, PyCharm, Sublime Text
  • Database: SQLite Browser, DBeaver
  • API Testing: Postman, Insomnia, curl
  • Docker: Docker Desktop, Portainer

Community


🎯 Getting Started Checklist

  • Fork and clone repository
  • Set up development environment
  • Run development server
  • Make a small test change
  • Read through existing code
  • Join community discussions
  • Find an issue to work on
  • Submit your first PR

Welcome to the Flowcase development community! 🎉

Clone this wiki locally