• v0.5.0 2b927864d5

    v0.5.0 Stable

    x released this 2026-02-22 21:59:37 +00:00 | 6 commits to main since this release

    Signed by x
    GPG key ID: A14ACA8AB45A9C27

    VSKI v0.5.0

    Features

    Complete OpenAPI Documentation for Migrations

    • Added OpenAPI Specs for All Migration Endpoints:

      • All migration endpoints now have complete OpenAPI 3.0 specifications
      • Properly documented with tags, summaries, operation IDs, and response schemas
      • Security requirements (BearerAuth) properly defined for all admin endpoints
      • Query parameters, request bodies, and responses fully specified
    • Endpoints Documented:

      • GET /api/migrations - List all migrations (applied and pending)
      • GET /api/migrations/status - Get the current status of the migration system
      • GET /api/migrations/generate - Generate a new migration file from schema diff
      • POST /api/migrations/save - Save a generated migration to a file
      • GET /api/migrations/load - Load a migration from a file
      • POST /api/migrations/upload - Upload a migration file
      • GET /api/migrations/export - Export all migrations as a ZIP file
      • POST /api/migrations/import - Import migrations from a ZIP file
      • GET /api/migrations/{version}/download - Download a specific migration file as YAML
      • POST /api/migrations/{version}/apply - Apply a migration
      • POST /api/migrations/{version}/rollback - Rollback a migration
      • DELETE /api/migrations/{version} - Delete a migration file and record

    New Migrations Management UI

    • Complete Web Interface for Migration Management:

      • New Migrations page accessible at /migrations in the web dashboard
      • Full CRUD operations for database migrations
      • Visual status indicators (pending, applied, failed)
    • Key Features:

      • Migration Status Overview: Shows applied and pending migration counts, last applied version
      • Generate Migrations: One-click generation from schema diffs
      • Apply/Rollback Migrations: Easy controls to apply or undo migrations
      • View Migration Details: View migration YAML files in a modal, with loading feedback
      • Download Migrations: Export individual migrations as YAML files
      • Export All Migrations: Bundle all migrations as a ZIP file (includes full YAML content)
      • Upload Migrations: Import migration files from local system
      • Delete Migrations: Remove unwanted migrations with confirmation
    • User Experience:

      • Pending migrations shown at the top with prominent Apply buttons
      • Applied migrations show rollback options and expandable details view with YAML content
      • Real-time status updates after actions
      • Loading states for all async operations
      • Confirmation dialogs for destructive actions (apply, rollback, delete)
      • Error and success notifications for all operations
    • Navigation:

      • Migrations link added to SQL section in sidebar navigation
      • Accessible via SQL → Migrations in the dashboard menu

    Code Refactoring

    Unified App Initialization

    • Common App Framework:

      • Created internal/app/bootstrap.go with shared initialization logic
      • NewApp(cfg *config.Config) function returns configured *gin.Engine and a cleanup function
      • Eliminated ~90% code duplication between cmd/server and cmd/standalone
      • Both entry points now use the same initialization
    • NewApp Function Output:

      • NewApp returns (*gin.Engine, func(), error)
      • The *gin.Engine provides access to the Gin router for custom routes.
      • The func() is a cleanup function to gracefully shut down services (e.g., cron, stats, database connections).
    • Mode-Specific Differences:

      • Server Mode (cmd/server/main.go): Uses NewApp and starts the Gin server.
      • Standalone Mode (cmd/standalone/main.go): Uses NewApp, then registers an router.NoRoute() handler to serve the embedded web UI. All API routes are available in both modes.

    Installation

    Binary

    Download binary for your platform:

    # Linux (standalone with embedded UI)
    wget https://git.vski.sh/x/platform/releases/download/v0.5.0/vski-standalone
    chmod +x vski-standalone
    ./vski-standalone
    
    # Linux (API-only)
    wget https://git.vski.sh/x/platform/releases/download/v0.5.0/vski
    chmod +x vski
    ./vski
    

    Docker

    Pull and run official Docker image:

    # Light version (API only, no embedded UI)
    docker pull git.vski.sh/x/vski:latest
    
    # Standalone version (with embedded web UI)
    docker pull git.vski.sh/x/vski:latest-standalone
    
    # Pull specific version
    docker pull git.vski.sh/x/vski:v0.5.0
    docker pull git.vski.sh/x/vski:v0.5.0-standalone
    
    # Run light version with default configuration
    docker run -p 3000:3000 -v $(pwd)/data:/app/data git.vski.sh/x/vski:latest
    
    # Run standalone version with embedded web UI
    docker run -p 3000:3000 -v $(pwd)/data:/app/data git.vski.sh/x/vski:latest-standalone
    

    Configuration

    Create a .env file:

    DATA_DIR=./data
    SERVER_PORT=3000
    JWT_SECRET=your-secret-key
    

    Usage

    Accessing OpenAPI Documentation

    The complete OpenAPI 3.0 specification is available at:

    GET /api/openapi.json
    

    This includes all endpoints including the newly documented migration APIs.

    Managing Migrations via Web UI

    Access the Migrations management page by navigating to:

    • URL: http://localhost:3000/migrations
    • Navigation: SQL → Migrations in the sidebar

    Common Workflows:

    1. Generate a new migration:

      • Click the "Generate" button to create a migration from current schema diff.
      • The new migration will appear in the "Pending Migrations" section and be saved to the data/migrations directory.
    2. Apply a pending migration:

      • Find the migration in the "Pending Migrations" list.
      • Click the "Apply" button.
      • Confirm the action when prompted.
      • The migration will move to "Applied Migrations" after successful application.
    3. Rollback an applied migration:

      • Find the migration in the "Applied Migrations" list.
      • Click the "Rollback" button.
      • Confirm the rollback action.
      • The migration will be reverted to its previous state.
    4. View migration details:

      • Click the JSON icon (view details) on any migration to display its YAML content in a modal.
      • For applied migrations, click the expand chevron (>) to view the YAML content inline.
    5. Export migrations:

      • Single migration: Click "Download" on a specific migration to save its YAML content.
      • All migrations: Click "Export All" to download all migrations as a ZIP file containing individual YAML files.
    6. Import migrations:

      • Click "Upload" button.
      • Select a migration YAML file or a ZIP file containing multiple migration YAMLs.
      • Click "Upload" to import the migration(s).

    Custom Router Integration

    You can now extend the VSKI server with custom routes using the NewApp function:

    package main
    
    import (
        "fmt"
        "git.vski.sh/x/vski/internal/app"
        "git.vski.sh/x/vski/internal/config"
        "github.com/gin-gonic/gin"
        "log"
        "os"
        "os/signal"
        "syscall"
    )
    
    func main() {
        cfg := config.Load()
        router, cleanup, err := app.NewApp(cfg)
        if err != nil {
            log.Fatalf("Failed to initialize app: %v", err)
        }
        defer cleanup()
        
        // Add custom routes
        router.GET("/custom", func(c *gin.Context) {
            c.JSON(200, gin.H{"message": "Custom endpoint"})
        })
        
        log.Printf("Starting custom server on port %s", cfg.ServerPort)
        go func() {
            if err := router.Run(fmt.Sprintf(":%s", cfg.ServerPort)); err != nil {
                log.Fatalf("Failed to start server: %v", err)
            }
        }()
    
        quit := make(chan os.Signal, 1)
        signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
        <-quit
    
        log.Println("Shutting down server...")
    }
    

    Migration Notes

    Database Migration Required

    Adding fieldRules Column to collections Table

    For existing deployments, you need to manually add the fieldRules column to the collections table. Run the following SQL in your database:

    ALTER TABLE collections ADD COLUMN fieldRules TEXT;
    

    Note: This change is backward compatible. Existing collections will have fieldRules set to NULL, which the application treats as "no field rules defined" (default open behavior).

    Adding fieldEditRules Column to collections Table

    For existing deployments, you need to manually add the fieldEditRules column to the collections table. Run the following SQL in your database:

    ALTER TABLE collections ADD COLUMN fieldEditRules TEXT;
    

    Note: This change is backward compatible. Existing collections will have fieldEditRules set to NULL, which the application treats as "no field edit rules defined" (default open behavior).

    Alternative: Automatic Schema Update

    If you prefer automatic schema updates, you can restart the VSKI server which will apply any missing columns during database initialization. The fieldRules column will be automatically added on first startup.

    Breaking Changes

    None.

    API Changes

    New OpenAPI Endpoints Documented

    The following migration endpoints now have complete OpenAPI specifications:

    • GET /api/migrations - List all migrations (applied and pending)
    • GET /api/migrations/status - Get migration system status
    • GET /api/migrations/generate - Generate new migration from schema diff
    • POST /api/migrations/save - Save migration to file (admin utility)
    • GET /api/migrations/load - Load migration from file path (admin utility)
    • POST /api/migrations/upload - Upload migration YAML file
    • GET /api/migrations/export - Export all migrations as ZIP
    • POST /api/migrations/import - Import migrations from ZIP file
    • GET /api/migrations/{version}/download - Download migration YAML
    • POST /api/migrations/{version}/apply - Apply migration
    • POST /api/migrations/{version}/rollback - Rollback migration
    • DELETE /api/migrations/{version} - Delete migration

    Migration File Format Changes

    Collection Changes Model (internal/models/migrations.go):

    • Added drop_columns: []string field to track removed columns
    • Supports soft delete - columns removed from metadata but kept in database

    Schema Diff Service (internal/services/schema_diff.go):

    • detectDropColumns() function identifies removed fields
    • isEmptyCollectionChanges() filters unchanged collections
    • Empty collection changes are excluded from generated migrations

    Migration Service (internal/services/migrations.go):

    • applyCollectionChanges() now handles drop_columns via metadata update only
    • Better error handling for rollback data parsing

    Code Structure Changes

    Before (separate main.go files):

    cmd/server/main.go    # ~220 lines
    cmd/standalone/main.go # ~205 lines
    

    After (shared initialization):

    internal/app/bootstrap.go   # New file with common logic
    cmd/server/main.go          # ~35 lines (uses app.NewApp(cfg))
    cmd/standalone/main.go      # ~40 lines (uses app.NewApp(cfg) + standalone handler)
    

    Testing

    Run E2E Tests

    cd vski
    make build
    make e2e TEST=50_migrations_test.ts  # Test migrations
    make e2e                             # Run all E2E tests
    

    Verify OpenAPI Spec

    # Build the server
    make build
    
    # Start the server
    ./bin/vski-prod
    
    # Fetch OpenAPI spec
    curl http://localhost:3000/api/openapi.json | jq '.paths | keys | .[] | select(contains("migrations"))'
    

    Expected output (example, actual list may vary slightly depending on other APIs):

    "/api/migrations"
    "/api/migrations/status"
    "/api/migrations/generate"
    "/api/migrations/save"
    "/api/migrations/load"
    "/api/migrations/upload"
    "/api/migrations/export"
    "/api/migrations/import"
    "/api/migrations/{version}/download"
    "/api/migrations/{version}/apply"
    "/api/migrations/{version}/rollback"
    "/api/migrations/{version}"
    

    Test Custom Router Integration

    package main
    
    import (
        "fmt"
        "git.vski.sh/x/vski/internal/app"
        "git.vski.sh/x/vski/internal/config"
        "github.com/gin-gonic/gin"
        "log"
        "os"
        "os/signal"
        "syscall"
        "net/http" // Added for example
    )
    
    func main() {
        cfg := config.Load()
        router, cleanup, err := app.NewApp(cfg)
        if err != nil {
            log.Fatalf("Failed to initialize app: %v", err)
        }
        defer cleanup()
        
        // Add custom routes
        router.GET("/custom", func(c *gin.Context) {
            c.JSON(200, gin.H{"message": "Custom endpoint"})
        })
        
        serverPort := cfg.ServerPort // Get the configured port
        
        log.Printf("Starting custom server on port %s", serverPort)
        go func() {
            if err := router.Run(fmt.Sprintf(":%s", serverPort)); err != nil {
                log.Fatalf("Failed to start server: %v", err)
            }
        }()
    
        // Example of calling a custom route
        time.Sleep(2 * time.Second) // Give server time to start
        resp, err := http.Get(fmt.Sprintf("http://localhost:%s/custom", serverPort))
        if err != nil {
            log.Printf("Error calling custom route: %v", err)
        } else {
            defer resp.Body.Close()
            if resp.StatusCode == http.StatusOK {
                log.Println("Custom route works!")
            } else {
                log.Printf("Custom route returned status: %d", resp.StatusCode)
            }
        }
    
        quit := make(chan os.Signal, 1)
        signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
        <-quit
    
        log.Println("Shutting down server...")
    }
    

    Run this code:

    go run your_custom_main.go
    

    Expected output will show "Custom route works!" if successful.

    Documentation

    Full documentation available at: https://git.vski.sh/x/platform

    Changelog

    v0.5.0 (2026-02-22)

    Added

    • Complete OpenAPI 3.0 specifications for all implemented migration endpoints.
    • internal/app/bootstrap.go with NewApp(cfg *config.Config) for unified app initialization.
    • Full UI for migration CRUD operations (generate, apply, rollback, view, download, upload, export, delete) in web/src/pages/Migrations.tsx.
    • Migration status overview, pending/applied migration counts, and last applied version in UI.
    • Inline display of migration YAML content for applied migrations via expandable details.
    • Loading feedback for asynchronous UI operations in migration management.
    • Dropped column tracking in migrations (drop_columns field in collection changes).
    • Soft delete for columns - dropped columns are untracked from metadata but remain in database (SQLite limitation workaround).
    • Filtering of empty collection changes - generated migrations no longer include empty collection entries.
    • Better error handling when parsing rollback data and generating migrations.
    • Field-level view rules (fieldRules) - Control READ visibility of individual fields by user groups.
    • Field-level edit rules (fieldEditRules) - Control CREATE/UPDATE access to individual fields by user groups.
    • Field-level permissions override global viewRule/createRule/updateRule when defined.
    • Split field permissions UI - Separate "View Rules" and "Edit Rules" sections in collection settings with clear visual indicators.
    • Required field warnings - UI shows warnings when required fields are not in a group's edit allowed list.

    Changed

    • Refactored cmd/server/main.go and cmd/standalone/main.go to use app.NewApp, significantly reducing code duplication.
    • Moved RecoveryMiddleware to internal/middleware/middleware.go for shared use.
    • DownloadMigrationsZip in internal/services/migrations.go now correctly includes full YAML content of migration files in the ZIP archive and uses proper version matching.
    • Corrected display logic for "View" migration YAML in web UI modal.
    • Removed duplicated UI buttons in web/src/pages/Migrations.tsx.

    Technical

    • Implemented backend migration APIs for list, status, generate, save, load, upload, export, import, download, apply, rollback, delete (global operations, not collection-specific).
    • App initialization is centralized in internal/app/bootstrap.go.
    • NewApp returns (*gin.Engine, func(), error) allowing flexible router extension and proper resource cleanup.
    • Standalone mode (cmd/standalone/main.go) integrates embedded web UI via router.NoRoute().
    • Server mode (cmd/server/main.go) serves to API without embedded UI.
    • Frontend MigrationsPage uses sdk.settings.migrations API for all operations with improved user feedback.
    • Added DropColumns field to CollectionChanges struct in internal/models/migrations.go.
    • Added detectDropColumns() function in internal/services/schema_diff.go to identify removed fields.
    • Added isEmptyCollectionChanges() helper function to filter out unchanged collections.
    • Updated applyCollectionChanges() in internal/services/migrations.go to handle dropped columns via soft delete.
    • Improved GenerateMigrationWithVersion() error handling for rollback data parsing.
    Downloads