← Back to Modules

dbbasic-admin Specification

Version: 0.1.0 Status: Draft Author: DBBASIC Team Date: 2025-01-17

Links: - PyPI: https://pypi.org/project/dbbasic-admin/ (pending) - GitHub: https://github.com/askrobots/dbbasic-admin (pending) - Specification: https://dbbasic.com/admin-spec


Philosophy

Admin interfaces should auto-generate from installed modules, not require manual configuration.

Core Principles: 1. Auto-discovery - Installing a dbbasic module automatically adds its admin panels 2. Convention over configuration - Modules just need admin.py and templates/admin/ 3. Filesystem routing - Admin routes follow file paths (like CGI/PHP) 4. No database required - Module registry and config stored in flat files 5. Composable - Each module owns its admin UI, dbbasic-admin just discovers and renders


Architecture Decision History

Problem 1: Static Admin Interfaces

Traditional approach:

# Manual route registration - breaks modularity
admin.register_route('/admin/posts', posts_handler)
admin.register_route('/admin/products', products_handler)
admin.register_route('/admin/comments', comments_handler)

Problems: - Installing a new module requires updating admin config - Admin interface tightly coupled to specific modules - No way to add admin panels without modifying core code

Problem 2: Dynamic Discovery with Route Tables

Flask/Django approach:

# Each module registers routes programmatically
from admin import register_blueprint
register_blueprint(posts_blueprint)

Problems: - Requires running registration code on startup - Route conflicts must be managed - Order of registration matters

Solution: Filesystem-based Auto-discovery

DBBASIC approach: 1. Modules export admin.py with ADMIN_CONFIG 2. Modules include templates/admin/ with HTML files 3. dbbasic-web's filesystem router handles routing automatically 4. Admin sidebar auto-populates from discovered modules

Benefits: - Zero configuration - just install the module - No registration code needed - File structure = URL structure - Works with dbbasic-web's existing routing


What Is an Admin Interface?

An admin interface is: 1. Navigation sidebar - Links to all admin sections 2. CRUD operations - Create, read, update, delete for data tables 3. Search & filters - Find and filter records 4. Dashboard - Overview of system status

dbbasic-admin provides: - Core admin pages (Dashboard, Code, Database, Settings, Jobs, Logs) - Auto-discovery of module admin pages - Auto-generated CRUD for TSV tables - Dynamic sidebar rendering


Module Discovery

How Modules Are Discovered

import importlib
import pkgutil

def discover_admin_modules():
    """Scan installed packages for admin configs"""
    modules = []

    for pkg in pkgutil.iter_modules():
        if pkg.name.startswith('dbbasic_'):
            try:
                admin_mod = importlib.import_module(f'{pkg.name}.admin')
                if hasattr(admin_mod, 'ADMIN_CONFIG'):
                    modules.append({
                        'name': pkg.name,
                        'config': admin_mod.ADMIN_CONFIG
                    })
            except (ImportError, AttributeError):
                pass  # Module has no admin interface

    return modules

Module Admin Config

Each module exports an admin.py file:

# dbbasic_blog/admin.py
ADMIN_CONFIG = [
    {
        "icon": "📝",
        "label": "Posts",
        "href": "/admin/posts",
        "order": 20,
        "table": "posts",  # Optional: auto-generates CRUD
    },
    {
        "icon": "💬",
        "label": "Comments",
        "href": "/admin/comments",
        "order": 21,
        "table": "comments",
    }
]

Filesystem Structure

Core admin module:

dbbasic_admin/
├── __init__.py
├── admin.py              # Discovery and registry
├── api/
│   └── nav.py           # Returns nav items as JSON
└── templates/
    └── admin/
        ├── index.html    # Dashboard
        ├── code.html     # Code editor
        ├── database.html # Database browser
        ├── jobs.html     # Background jobs
        ├── logs.html     # System logs
        └── settings.html # Settings

Module with admin pages:

dbbasic_blog/
├── __init__.py
├── admin.py             # Exports ADMIN_CONFIG
└── templates/
    └── admin/
        ├── posts/
        │   ├── list.html    # /admin/posts/list
        │   ├── new.html     # /admin/posts/new
        │   └── [id].html    # /admin/posts/123 (pattern routing)
        └── comments/
            └── list.html    # /admin/comments/list

API Specification

discover_modules()

Purpose: Find all installed dbbasic modules with admin interfaces

Signature:

def discover_modules() -> List[Dict[str, Any]]

Returns:

[
    {
        'name': 'dbbasic_blog',
        'config': [
            {'icon': '📝', 'label': 'Posts', 'href': '/admin/posts', 'order': 20}
        ]
    },
    {
        'name': 'dbbasic_commerce',
        'config': [
            {'icon': '🛒', 'label': 'Products', 'href': '/admin/products', 'order': 30}
        ]
    }
]

Caching: Results cached for 60 seconds to avoid repeated filesystem scans


build_nav(modules)

Purpose: Generate sidebar navigation from discovered modules

Signature:

def build_nav(modules: List[Dict]) -> List[Dict[str, Any]]

Returns:

[
    # Core items (always present)
    {'icon': '📊', 'label': 'Dashboard', 'href': '/admin/', 'order': 0},
    {'icon': '💻', 'label': 'Code', 'href': '/admin/code', 'order': 10},
    {'icon': '🗄️', 'label': 'Database', 'href': '/admin/database', 'order': 11},

    # Module items (from ADMIN_CONFIG)
    {'icon': '📝', 'label': 'Posts', 'href': '/admin/posts', 'order': 20},
    {'icon': '🛒', 'label': 'Products', 'href': '/admin/products', 'order': 30},

    # System items (always at bottom)
    {'icon': '⏰', 'label': 'Jobs', 'href': '/admin/jobs', 'order': 90},
    {'icon': '📋', 'label': 'Logs', 'href': '/admin/logs', 'order': 91},
    {'icon': '⚙️', 'label': 'Settings', 'href': '/admin/settings', 'order': 99},
]

generate_crud(table_name)

Purpose: Auto-generate CRUD interface for a TSV table

Signature:

def generate_crud(table_name: str, config: Dict = None) -> str

Parameters: - table_name: Name of TSV table in _data/ - config: Optional configuration for fields, labels, validation

Returns: HTML for list, create, edit, delete pages

Example:

# Automatically generates:
# - /admin/posts/list.html  (list view with search/filter)
# - /admin/posts/new.html   (create form)
# - /admin/posts/[id].html  (edit form)

Implementation

Core Discovery (~50 lines)

# dbbasic_admin/admin.py
import importlib
import pkgutil
from typing import List, Dict, Any
from functools import lru_cache

# Core admin navigation (always present)
CORE_NAV = [
    {'icon': '📊', 'label': 'Dashboard', 'href': '/admin/', 'order': 0},
    {'icon': '💻', 'label': 'Code', 'href': '/admin/code', 'order': 10},
    {'icon': '🗄️', 'label': 'Database', 'href': '/admin/database', 'order': 11},
    {'icon': '⏰', 'label': 'Jobs', 'href': '/admin/jobs', 'order': 90},
    {'icon': '📋', 'label': 'Logs', 'href': '/admin/logs', 'order': 91},
    {'icon': '⚙️', 'label': 'Settings', 'href': '/admin/settings', 'order': 99},
]

@lru_cache(maxsize=1)
def discover_modules() -> List[Dict[str, Any]]:
    """Auto-discover admin modules from installed packages"""
    modules = []

    for pkg in pkgutil.iter_modules():
        if pkg.name.startswith('dbbasic_') and pkg.name != 'dbbasic_admin':
            try:
                admin_mod = importlib.import_module(f'{pkg.name}.admin')
                if hasattr(admin_mod, 'ADMIN_CONFIG'):
                    modules.append({
                        'name': pkg.name,
                        'config': admin_mod.ADMIN_CONFIG
                    })
            except (ImportError, AttributeError):
                continue

    return modules

def build_nav() -> List[Dict[str, Any]]:
    """Build complete navigation from core + discovered modules"""
    nav_items = CORE_NAV.copy()

    # Add module nav items
    for module in discover_modules():
        nav_items.extend(module['config'])

    # Sort by order
    nav_items.sort(key=lambda x: x.get('order', 50))

    return nav_items

def clear_cache():
    """Clear discovery cache (call after installing new module)"""
    discover_modules.cache_clear()

API Endpoint

# dbbasic_admin/api/nav.py
import json
from dbbasic_admin.admin import build_nav
from dbbasic_web.responses import json as json_response

def GET(request):
    """Return navigation items as JSON"""
    nav = build_nav()
    return json_response(json.dumps(nav))

Usage Examples

Installing dbbasic-blog Adds Posts Admin

1. Install the module:

pip install dbbasic-blog

2. Module includes admin.py:

# dbbasic_blog/admin.py
ADMIN_CONFIG = [
    {'icon': '📝', 'label': 'Posts', 'href': '/admin/posts', 'order': 20}
]

3. Admin sidebar automatically shows "Posts":

Dashboard
Code
Database
Posts         ← Automatically added!
Jobs
Logs
Settings

4. No configuration needed! Just install and it appears.


Auto-generating CRUD for Custom Tables

# dbbasic_myapp/admin.py
ADMIN_CONFIG = [
    {
        'icon': '📦',
        'label': 'Inventory',
        'href': '/admin/inventory',
        'order': 25,
        'table': 'inventory',  # Auto-generates CRUD
        'fields': {
            'name': {'label': 'Product Name', 'required': True},
            'sku': {'label': 'SKU', 'required': True},
            'quantity': {'label': 'Quantity', 'type': 'number'},
            'price': {'label': 'Price', 'type': 'number', 'min': 0},
        }
    }
]

This automatically creates: - List view with search and filtering - Create form with validation - Edit form - Delete confirmation


Custom Admin Handlers

For complex admin pages, provide a custom handler:

# dbbasic_commerce/admin.py
ADMIN_CONFIG = [
    {
        'icon': '📊',
        'label': 'Sales Dashboard',
        'href': '/admin/sales',
        'order': 35,
        'custom': True  # No auto-CRUD
    }
]

# dbbasic_commerce/templates/admin/sales.html
# Custom template with charts, graphs, etc.

Performance Characteristics

Discovery Performance

Operation Time
Module discovery (cold) ~50ms
Module discovery (cached) ~0.1ms
Nav building ~1ms
Total page load impact <5ms

Caching strategy: - Discovery results cached with @lru_cache - Cache invalidated on module install/uninstall - 60-second TTL for development mode

Scalability


Features

Core Features

  1. Auto-discovery - Finds installed modules automatically
  2. Dynamic sidebar - Renders nav from core + modules
  3. Auto-CRUD - Generates admin UI for TSV tables
  4. Search & filters - Built-in for all tables
  5. Filesystem routing - Uses dbbasic-web's routing
  6. No configuration - Works out of the box

Admin Pages Included


Dependencies

[dependencies]
python = "^3.9"
dbbasic-web = "^0.1.7"
dbbasic-tsv = "^0.1.0"
jinja2 = "^3.1.0"

Deployment

Installation

pip install dbbasic-admin

Setup in dbbasic-web Project

1. Include admin routes:

# asgi.py
from dbbasic_admin import include_admin_routes

app = create_app()
include_admin_routes(app)  # Adds /admin/* routes

2. That's it! Admin is available at /admin/

Installing Modules with Admin

pip install dbbasic-blog
# Admin automatically shows "Posts" tab

ADMIN_CONFIG Reference

Full Configuration Options

ADMIN_CONFIG = [
    {
        # Required fields
        'label': 'Posts',           # Display name
        'href': '/admin/posts',     # URL path

        # Optional fields
        'icon': '📝',              # Emoji or icon class
        'order': 20,               # Sort order (0-99)
        'badge': '3',              # Notification badge
        'divider': False,          # Show divider before this item

        # Auto-CRUD options
        'table': 'posts',          # TSV table name
        'fields': {                # Field configuration
            'title': {
                'label': 'Title',
                'required': True,
                'maxlength': 200
            },
            'content': {
                'label': 'Content',
                'type': 'textarea',
                'rows': 10
            }
        },

        # Advanced options
        'custom': True,            # Disable auto-CRUD
        'permissions': ['admin'],   # Required permissions
    }
]

Architectural Decisions

Why Auto-discovery?

Alternatives considered:

  1. Manual registration python admin.register('Posts', '/admin/posts') # ❌ Requires config

  2. Config files ```yaml admin:

    • label: Posts href: /admin/posts # ❌ Separate config to maintain ```
  3. Auto-discovery python # Module just exports ADMIN_CONFIG ✅ Zero config!

Decision: Auto-discovery wins because it requires zero configuration and follows the Unix philosophy of composability.


Why Filesystem Routing?

Aligns with dbbasic-web's philosophy: - File structure mirrors URL structure - No route tables to maintain - Easy to understand and debug - Works with existing dbbasic-web routing


Why Order Numbers vs Groups?

Order numbers (0-99):

{'label': 'Dashboard', 'order': 0}
{'label': 'Posts', 'order': 20}
{'label': 'Settings', 'order': 99}

vs Groups:

{'label': 'Dashboard', 'group': 'core'}
{'label': 'Posts', 'group': 'content'}

Decision: Order numbers are simpler and give precise control over positioning.


Security Considerations

Module Trust

Access Control

Admin pages should check authentication:

# In each admin template/handler
from dbbasic_sessions import get_session

def GET(request):
    session = get_session(request)
    if not session or not session.get('is_admin'):
        return redirect('/login')
    # ... render admin page

CSRF Protection

Forms should include CSRF tokens:

<form method="post">
    <input type="hidden" name="csrf_token" value="{{ csrf_token }}">
    <!-- ... form fields ... -->
</form>

Testing Requirements

Unit Tests

def test_discover_modules():
    """Test module discovery"""
    modules = discover_modules()
    assert isinstance(modules, list)

def test_build_nav():
    """Test nav building"""
    nav = build_nav()
    assert len(nav) >= 6  # At least core items
    assert nav[0]['label'] == 'Dashboard'

def test_nav_ordering():
    """Test nav items are sorted by order"""
    nav = build_nav()
    orders = [item.get('order', 50) for item in nav]
    assert orders == sorted(orders)

Integration Tests

def test_admin_nav_endpoint():
    """Test /admin/api/nav returns JSON"""
    response = client.get('/admin/api/nav')
    assert response.status_code == 200
    nav = json.loads(response.body)
    assert isinstance(nav, list)

Comparison to Alternatives

Feature dbbasic-admin Django Admin Flask-Admin Rails Admin
Auto-discovery ✅ Zero config ❌ Manual registration ❌ Manual setup ❌ Manual setup
Filesystem routing ✅ Yes ❌ URL patterns ❌ Route decorators ❌ Route files
Auto-CRUD ✅ From TSV ✅ From models ✅ From models ✅ From models
Database required ❌ No ✅ Yes ✅ Yes ✅ Yes
Lines of code ~500 ~50,000 ~10,000 ~20,000
Module plugins ✅ Auto-detected ❌ Manual ❌ Manual ❌ Manual

When to Use Something Else

Use Django Admin when: - You need a mature, battle-tested admin - You're already using Django ORM - You need complex permissions and workflows

Use dbbasic-admin when: - You want zero-configuration module discovery - You're using TSV files instead of SQL - You want a lightweight admin (<500 lines) - You want filesystem-based routing


Migration Guide

From Manual Admin Routes

Before:

# app.py
admin.add_route('/admin/posts', posts_handler)
admin.add_route('/admin/comments', comments_handler)

After:

# dbbasic_blog/admin.py
ADMIN_CONFIG = [
    {'label': 'Posts', 'href': '/admin/posts', 'order': 20},
    {'label': 'Comments', 'href': '/admin/comments', 'order': 21},
]

No registration code needed!


Future Enhancements


References


Summary

dbbasic-admin provides a zero-configuration admin interface that auto-discovers installed modules and generates CRUD interfaces for TSV tables.

Key innovations: 1. Auto-discovery - Just install a module, admin tab appears 2. Filesystem routing - Files define routes (like CGI/PHP) 3. Composable - Each module owns its admin UI 4. Lightweight - ~500 lines total

Next steps: 1. Install: pip install dbbasic-admin 2. Access: http://localhost:8000/admin/ 3. Install modules: pip install dbbasic-blog (auto-adds "Posts" tab)

Built for simplicity, composability, and the Unix philosophy.