# b0esche.cloud Development Guide

This guide covers local development setup, coding conventions, and contribution guidelines.

## Prerequisites

### Required Software

| Software | Version | Installation |
|----------|---------|--------------|
| Go | 1.21+ | `brew install go` |
| Flutter | 3.10+ | [flutter.dev](https://flutter.dev/docs/get-started/install) |
| Docker | 24+ | [docker.com](https://docker.com) |
| PostgreSQL | 15+ | `brew install postgresql@15` or Docker |
| Git | 2.x | `brew install git` |

### Recommended Tools

- **VS Code** with extensions:
  - Go
  - Flutter
  - Dart
  - Docker
  - GitLens
- **TablePlus** or **DBeaver** for database management
- **Postman** or **Bruno** for API testing

## Project Setup

### 1. Clone the Repository

```bash
git clone https://lab.b0esche.cloud/b0esche/b0esche_cloud.git
cd b0esche_cloud
```

### 2. Backend Setup

```bash
cd go_cloud

# Copy environment file
cp .env.example .env

# Edit .env with your local settings
# Key variables to set:
# - DATABASE_URL=postgres://user:pass@localhost:5432/b0esche_dev?sslmode=disable
# - JWT_SECRET=your-dev-secret
# - WEBAUTHN_RP_ID=localhost
# - WEBAUTHN_RP_ORIGIN=http://localhost:8080
```

#### Start PostgreSQL

**Option A: Using Docker (Recommended)**
```bash
docker run -d \
  --name b0esche-postgres \
  -e POSTGRES_USER=b0esche \
  -e POSTGRES_PASSWORD=devpassword \
  -e POSTGRES_DB=b0esche_dev \
  -p 5432:5432 \
  postgres:15-alpine
```

**Option B: Using local PostgreSQL**
```bash
createdb b0esche_dev
```

#### Run Migrations

```bash
# Install goose
go install github.com/pressly/goose/v3/cmd/goose@latest

# Run migrations
goose -dir migrations postgres "$DATABASE_URL" up
```

#### Start Backend

```bash
# Development mode with hot reload
go run ./cmd/api

# Or build and run
go build -o bin/api ./cmd/api
./bin/api
```

The backend will be available at `http://localhost:8080`.

### 3. Frontend Setup

```bash
cd b0esche_cloud

# Get dependencies
flutter pub get

# Run in Chrome (recommended for web development)
flutter run -d chrome

# Or run with specific port
flutter run -d chrome --web-port=3000
```

The frontend will be available at `http://localhost:3000` (or the port shown).

### 4. Quick Start Script

Use the provided development script:

```bash
./scripts/dev-all.sh
```

This starts all services in the correct order.

## Project Structure

### Backend (`go_cloud/`)

```
go_cloud/
├── cmd/
│   └── api/
│       └── main.go           # Application entry point
├── internal/
│   ├── auth/
│   │   ├── auth.go           # Authentication service
│   │   ├── passkey.go        # WebAuthn implementation
│   │   └── auth_test.go      # Tests
│   ├── config/
│   │   └── config.go         # Configuration loading
│   ├── database/
│   │   └── database.go       # Database connection
│   ├── files/
│   │   └── files.go          # File operations
│   ├── http/
│   │   ├── routes.go         # Route definitions
│   │   ├── server.go         # HTTP server setup
│   │   └── wopi_handlers.go  # WOPI protocol handlers
│   ├── middleware/
│   │   └── middleware.go     # HTTP middleware
│   ├── models/
│   │   └── *.go              # Data models
│   ├── org/
│   │   └── org.go            # Organization logic
│   ├── storage/
│   │   ├── nextcloud.go      # Nextcloud integration
│   │   └── webdav.go         # WebDAV client
│   └── ...
├── migrations/
│   ├── 0001_initial.sql
│   ├── 0002_passkeys.sql
│   └── ...
├── pkg/
│   └── jwt/
│       └── jwt.go            # JWT utilities
├── .env.example
├── Dockerfile
├── go.mod
└── Makefile
```

### Frontend (`b0esche_cloud/`)

```
b0esche_cloud/
├── lib/
│   ├── main.dart             # App entry point
│   ├── injection.dart        # Dependency injection
│   ├── blocs/
│   │   ├── auth/
│   │   │   ├── auth_bloc.dart
│   │   │   ├── auth_event.dart
│   │   │   └── auth_state.dart
│   │   ├── files/
│   │   └── org/
│   ├── models/
│   │   ├── user.dart
│   │   ├── file.dart
│   │   └── organization.dart
│   ├── pages/
│   │   ├── home_page.dart
│   │   ├── files_page.dart
│   │   ├── settings_page.dart
│   │   └── admin/
│   ├── repositories/
│   │   ├── auth_repository.dart
│   │   └── file_repository.dart
│   ├── services/
│   │   ├── api_client.dart
│   │   └── webauthn_service.dart
│   ├── theme/
│   │   └── app_theme.dart
│   └── widgets/
│       ├── file_list.dart
│       └── ...
├── web/
│   └── index.html
├── pubspec.yaml
└── analysis_options.yaml
```

## Coding Conventions

### Go Backend

#### Code Style
- Follow [Effective Go](https://golang.org/doc/effective_go)
- Use `gofmt` for formatting
- Use `golint` and `go vet` for linting

```bash
# Format code
gofmt -w .

# Lint
golint ./...
go vet ./...
```

#### Naming Conventions
- **Packages**: lowercase, single word (`auth`, `files`)
- **Exported functions**: PascalCase (`CreateUser`)
- **Private functions**: camelCase (`validateToken`)
- **Constants**: PascalCase (`DefaultTimeout`)

#### Error Handling
```go
// Always handle errors explicitly
user, err := s.GetUser(ctx, id)
if err != nil {
    return nil, fmt.Errorf("failed to get user: %w", err)
}

// Use custom error types for API errors
type APIError struct {
    Code    string `json:"code"`
    Message string `json:"message"`
}
```

#### Project Patterns
```go
// Service pattern
type AuthService struct {
    db     *sqlx.DB
    config *config.Config
}

func NewAuthService(db *sqlx.DB, config *config.Config) *AuthService {
    return &AuthService{db: db, config: config}
}

// Handler pattern
func (h *Handler) HandleLogin(w http.ResponseWriter, r *http.Request) {
    // Parse request
    // Call service
    // Return response
}
```

### Flutter Frontend

#### Code Style
- Follow [Effective Dart](https://dart.dev/guides/language/effective-dart)
- Use `dart format` for formatting
- Use `dart analyze` for linting

```bash
# Format code
dart format .

# Analyze
dart analyze
flutter analyze
```

#### Naming Conventions
- **Classes**: PascalCase (`AuthBloc`)
- **Files**: snake_case (`auth_bloc.dart`)
- **Variables/Functions**: camelCase (`getUserName`)
- **Constants**: camelCase or SCREAMING_CAPS

#### BLoC Pattern
```dart
// Events
abstract class AuthEvent {}
class LoginRequested extends AuthEvent {
  final String username;
  LoginRequested(this.username);
}

// States
abstract class AuthState {}
class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class AuthAuthenticated extends AuthState {
  final User user;
  AuthAuthenticated(this.user);
}
class AuthError extends AuthState {
  final String message;
  AuthError(this.message);
}

// BLoC
class AuthBloc extends Bloc<AuthEvent, AuthState> {
  AuthBloc() : super(AuthInitial()) {
    on<LoginRequested>(_onLoginRequested);
  }

  Future<void> _onLoginRequested(
    LoginRequested event,
    Emitter<AuthState> emit,
  ) async {
    emit(AuthLoading());
    try {
      final user = await _authRepository.login(event.username);
      emit(AuthAuthenticated(user));
    } catch (e) {
      emit(AuthError(e.toString()));
    }
  }
}
```

#### Widget Structure
```dart
class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<MyBloc, MyState>(
      builder: (context, state) {
        return switch (state) {
          MyLoading() => const CircularProgressIndicator(),
          MyLoaded(:final data) => _buildContent(data),
          MyError(:final message) => Text('Error: $message'),
          _ => const SizedBox.shrink(),
        };
      },
    );
  }
}
```

## Testing

### Backend Tests

```bash
cd go_cloud

# Run all tests
go test ./...

# Run with coverage
go test -cover ./...

# Run specific package
go test ./internal/auth/...

# Verbose output
go test -v ./...
```

Example test:
```go
func TestAuthService_Login(t *testing.T) {
    // Setup
    db := setupTestDB(t)
    service := NewAuthService(db, testConfig)

    // Test
    user, err := service.Login(context.Background(), "testuser")

    // Assert
    assert.NoError(t, err)
    assert.Equal(t, "testuser", user.Username)
}
```

### Frontend Tests

```bash
cd b0esche_cloud

# Run all tests
flutter test

# Run with coverage
flutter test --coverage

# Run specific test file
flutter test test/auth_bloc_test.dart

# Run integration tests
flutter test integration_test/
```

Example test:
```dart
void main() {
  group('AuthBloc', () {
    late AuthBloc authBloc;
    late MockAuthRepository mockRepository;

    setUp(() {
      mockRepository = MockAuthRepository();
      authBloc = AuthBloc(authRepository: mockRepository);
    });

    blocTest<AuthBloc, AuthState>(
      'emits [AuthLoading, AuthAuthenticated] on successful login',
      build: () => authBloc,
      act: (bloc) => bloc.add(LoginRequested('testuser')),
      expect: () => [
        AuthLoading(),
        isA<AuthAuthenticated>(),
      ],
    );
  });
}
```

## Database Migrations

### Creating a Migration

```bash
cd go_cloud

# Create new migration
goose -dir migrations create add_new_table sql
```

### Migration Best Practices

```sql
-- migrations/0005_add_feature.sql

-- +goose Up
-- Add new column with default
ALTER TABLE users ADD COLUMN new_field TEXT DEFAULT '';

-- Create index for performance
CREATE INDEX idx_users_new_field ON users(new_field);

-- +goose Down
DROP INDEX IF EXISTS idx_users_new_field;
ALTER TABLE users DROP COLUMN new_field;
```

### Running Migrations

```bash
# Apply all pending migrations
goose -dir migrations postgres "$DATABASE_URL" up

# Rollback last migration
goose -dir migrations postgres "$DATABASE_URL" down

# Check migration status
goose -dir migrations postgres "$DATABASE_URL" status
```

## Debugging

### Backend Debugging

**VS Code launch.json:**
```json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch Go Backend",
      "type": "go",
      "request": "launch",
      "mode": "debug",
      "program": "${workspaceFolder}/go_cloud/cmd/api",
      "envFile": "${workspaceFolder}/go_cloud/.env"
    }
  ]
}
```

**Logging:**
```go
import "log"

log.Printf("User login attempt: %s", username)
```

### Frontend Debugging

**Chrome DevTools:**
- Press F12 in Chrome
- Use the Flutter DevTools extension

**Debug print:**
```dart
debugPrint('Current state: $state');
```

**VS Code launch.json:**
```json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Flutter Web",
      "type": "dart",
      "request": "launch",
      "program": "lib/main.dart",
      "deviceId": "chrome"
    }
  ]
}
```

## Git Workflow

### Branch Naming

- `feature/description` - New features
- `fix/description` - Bug fixes
- `refactor/description` - Code refactoring
- `docs/description` - Documentation updates

### Commit Messages

Follow conventional commits:
```
type(scope): description

feat(auth): add passkey registration flow
fix(files): correct upload progress display
docs(readme): update deployment instructions
refactor(api): extract common error handling
```

### Pull Request Process

1. Create feature branch from `main`
2. Make changes with atomic commits
3. Run tests locally
4. Push and create PR
5. Wait for review
6. Squash and merge

## Troubleshooting

### Common Issues

#### Backend won't start
```bash
# Check if port is in use
lsof -i :8080

# Check database connection
psql $DATABASE_URL -c "SELECT 1"

# Check logs
go run ./cmd/api 2>&1 | head -50
```

#### Flutter build fails
```bash
# Clean and rebuild
flutter clean
flutter pub get
flutter run -d chrome

# Check for dependency issues
flutter pub deps
```

#### Database migration fails
```bash
# Check current status
goose -dir migrations postgres "$DATABASE_URL" status

# Force specific version
goose -dir migrations postgres "$DATABASE_URL" fix
```

#### WebAuthn not working locally
- WebAuthn requires HTTPS in production
- For localhost, use `WEBAUTHN_RP_ID=localhost`
- Chrome allows WebAuthn on localhost without HTTPS

## Environment Variables Reference

### Backend (.env)

```bash
# Server
SERVER_ADDR=:8080
DEV_MODE=true

# Database
DATABASE_URL=postgres://user:pass@localhost:5432/dbname?sslmode=disable

# Authentication
JWT_SECRET=your-secret-key
WEBAUTHN_RP_ID=localhost
WEBAUTHN_RP_NAME=b0esche.cloud
WEBAUTHN_RP_ORIGIN=http://localhost:8080

# External Services (optional for local dev)
NEXTCLOUD_BASE_URL=https://storage.b0esche.cloud
NEXTCLOUD_USERNAME=admin
NEXTCLOUD_PASSWORD=password
COLLABORA_BASE_URL=https://of.b0esche.cloud
```

### Frontend

API base URL is configured in `lib/services/api_client.dart`:
```dart
class ApiClient {
  // For development
  static const baseUrl = 'http://localhost:8080';
  
  // For production (set via build args)
  // static const baseUrl = String.fromEnvironment('API_URL');
}
```

## Resources

- [Go Documentation](https://golang.org/doc/)
- [Flutter Documentation](https://flutter.dev/docs)
- [WebAuthn Guide](https://webauthn.guide/)
- [BLoC Library](https://bloclibrary.dev/)
- [Chi Router](https://github.com/go-chi/chi)
