docs: add plugin distribution system plan for WP/Shopify/Custom integrations
Comprehensive plan covering:
- Directory structure: /plugins/{platform}/source/ and /dist/
- Database models: Plugin, PluginVersion, PluginInstallation
- API endpoints for download, check-update, register, health-check
- WordPress auto-update mechanism via pre_set_site_transient_update_plugins hook
- Build scripts for ZIP generation with versioning
- Security: signed URLs, checksums, rate limiting
- Future-ready for Shopify and custom site integrations
- Monitoring and analytics dashboard widgets
- 5-phase implementation roadmap
This commit is contained in:
692
docs/plans/PLUGIN-DISTRIBUTION-SYSTEM.md
Normal file
692
docs/plans/PLUGIN-DISTRIBUTION-SYSTEM.md
Normal file
@@ -0,0 +1,692 @@
|
||||
# IGNY8 Plugin Distribution System
|
||||
|
||||
**Created:** January 9, 2026
|
||||
**Version:** 1.0
|
||||
**Status:** Planning
|
||||
**Scope:** WordPress, Shopify, Custom Site Integration Plugins
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines the architecture for distributing and auto-updating IGNY8 integration plugins across multiple platforms (WordPress, Shopify, Custom Sites).
|
||||
|
||||
---
|
||||
|
||||
## 1. Directory Structure
|
||||
|
||||
### 1.1 - Server Location
|
||||
|
||||
```
|
||||
/data/app/igny8/
|
||||
├── backend/
|
||||
│ └── igny8_core/
|
||||
│ └── plugins/ # NEW: Plugin management
|
||||
│ ├── __init__.py
|
||||
│ ├── models.py # Plugin version tracking
|
||||
│ ├── views.py # Download & update API
|
||||
│ ├── serializers.py
|
||||
│ ├── urls.py
|
||||
│ └── utils.py # ZIP compression, versioning
|
||||
├── plugins/ # NEW: Plugin source & distributions
|
||||
│ ├── wordpress/
|
||||
│ │ ├── source/ # Development source files
|
||||
│ │ │ └── igny8-wp-bridge/
|
||||
│ │ │ ├── igny8-wp-bridge.php
|
||||
│ │ │ ├── includes/
|
||||
│ │ │ ├── assets/
|
||||
│ │ │ └── readme.txt
|
||||
│ │ └── dist/ # Compiled/zipped releases
|
||||
│ │ ├── igny8-wp-bridge-v1.0.0.zip
|
||||
│ │ ├── igny8-wp-bridge-v1.0.1.zip
|
||||
│ │ └── igny8-wp-bridge-latest.zip # Symlink to latest
|
||||
│ ├── shopify/ # FUTURE
|
||||
│ │ ├── source/
|
||||
│ │ └── dist/
|
||||
│ └── custom-site/ # FUTURE
|
||||
│ ├── source/
|
||||
│ └── dist/
|
||||
└── docs/
|
||||
└── plans/
|
||||
└── PLUGIN-DISTRIBUTION-SYSTEM.md
|
||||
```
|
||||
|
||||
### 1.2 - Why This Structure?
|
||||
|
||||
| Location | Purpose |
|
||||
|----------|---------|
|
||||
| `/plugins/` | Separate from app code, easy to manage |
|
||||
| `/plugins/{platform}/source/` | Development files, version controlled |
|
||||
| `/plugins/{platform}/dist/` | Ready-to-download ZIP files |
|
||||
| `/backend/igny8_core/plugins/` | Django app for API & version management |
|
||||
|
||||
---
|
||||
|
||||
## 2. Database Models
|
||||
|
||||
### 2.1 - Plugin Model
|
||||
|
||||
```python
|
||||
# backend/igny8_core/plugins/models.py
|
||||
|
||||
class Plugin(models.Model):
|
||||
"""Represents a plugin type (WordPress, Shopify, etc.)"""
|
||||
|
||||
PLATFORM_CHOICES = [
|
||||
('wordpress', 'WordPress'),
|
||||
('shopify', 'Shopify'),
|
||||
('custom', 'Custom Site'),
|
||||
]
|
||||
|
||||
name = models.CharField(max_length=100) # "IGNY8 WP Bridge"
|
||||
slug = models.SlugField(unique=True) # "igny8-wp-bridge"
|
||||
platform = models.CharField(max_length=20, choices=PLATFORM_CHOICES)
|
||||
description = models.TextField()
|
||||
is_active = models.BooleanField(default=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = 'plugins'
|
||||
|
||||
|
||||
class PluginVersion(models.Model):
|
||||
"""Tracks each version of a plugin"""
|
||||
|
||||
STATUS_CHOICES = [
|
||||
('draft', 'Draft'), # In development
|
||||
('testing', 'Testing'), # Internal testing
|
||||
('staged', 'Staged'), # Ready, not yet pushed
|
||||
('released', 'Released'), # Available for download
|
||||
('update_ready', 'Update Ready'), # Push to installed sites
|
||||
('deprecated', 'Deprecated'), # Old version
|
||||
]
|
||||
|
||||
plugin = models.ForeignKey(Plugin, on_delete=models.CASCADE, related_name='versions')
|
||||
version = models.CharField(max_length=20) # "1.0.0", "1.0.1"
|
||||
version_code = models.IntegerField() # 100, 101 (for comparison)
|
||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
|
||||
|
||||
# File info
|
||||
file_path = models.CharField(max_length=500) # Relative path in dist/
|
||||
file_size = models.IntegerField(default=0) # Bytes
|
||||
checksum = models.CharField(max_length=64) # SHA256 for integrity
|
||||
|
||||
# Release info
|
||||
changelog = models.TextField(blank=True)
|
||||
min_api_version = models.CharField(max_length=20, default='1.0')
|
||||
|
||||
# Timestamps
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
released_at = models.DateTimeField(null=True, blank=True)
|
||||
|
||||
# Auto-update control
|
||||
force_update = models.BooleanField(default=False) # Critical security fix
|
||||
|
||||
class Meta:
|
||||
db_table = 'plugin_versions'
|
||||
unique_together = ['plugin', 'version']
|
||||
ordering = ['-version_code']
|
||||
|
||||
|
||||
class PluginInstallation(models.Model):
|
||||
"""Tracks where plugins are installed (per site)"""
|
||||
|
||||
site = models.ForeignKey('Site', on_delete=models.CASCADE, related_name='plugin_installations')
|
||||
plugin = models.ForeignKey(Plugin, on_delete=models.CASCADE)
|
||||
current_version = models.ForeignKey(PluginVersion, on_delete=models.SET_NULL, null=True)
|
||||
|
||||
# Installation status
|
||||
is_active = models.BooleanField(default=True)
|
||||
last_health_check = models.DateTimeField(null=True)
|
||||
|
||||
# Update tracking
|
||||
pending_update = models.ForeignKey(
|
||||
PluginVersion,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
related_name='pending_installations'
|
||||
)
|
||||
update_notified_at = models.DateTimeField(null=True)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = 'plugin_installations'
|
||||
unique_together = ['site', 'plugin']
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. API Endpoints
|
||||
|
||||
### 3.1 - Public Endpoints (No Auth Required)
|
||||
|
||||
```python
|
||||
# Download latest plugin (for initial installation)
|
||||
GET /api/plugins/{slug}/download/
|
||||
# Returns: ZIP file download
|
||||
|
||||
# Check for updates (called by installed plugins)
|
||||
GET /api/plugins/{slug}/check-update/?current_version=1.0.0
|
||||
# Returns: { "update_available": true, "latest_version": "1.0.1", "download_url": "...", "changelog": "..." }
|
||||
```
|
||||
|
||||
### 3.2 - Authenticated Endpoints
|
||||
|
||||
```python
|
||||
# Get download URL for user (tracks downloads)
|
||||
GET /api/plugins/{slug}/download-url/
|
||||
# Returns: { "url": "...", "expires_at": "..." }
|
||||
|
||||
# Report installation (called after WP plugin is activated)
|
||||
POST /api/plugins/{slug}/register-installation/
|
||||
# Body: { "site_id": 123, "version": "1.0.0" }
|
||||
|
||||
# Update installation status (health check)
|
||||
POST /api/plugins/{slug}/health-check/
|
||||
# Body: { "site_id": 123, "version": "1.0.0", "status": "active" }
|
||||
```
|
||||
|
||||
### 3.3 - Admin Endpoints
|
||||
|
||||
```python
|
||||
# List all plugin versions
|
||||
GET /api/admin/plugins/
|
||||
|
||||
# Create new version
|
||||
POST /api/admin/plugins/{slug}/versions/
|
||||
# Body: { "version": "1.0.1", "changelog": "...", "status": "draft" }
|
||||
|
||||
# Upload plugin ZIP
|
||||
POST /api/admin/plugins/{slug}/versions/{version}/upload/
|
||||
# Body: multipart/form-data with ZIP file
|
||||
|
||||
# Release version (make available for download)
|
||||
POST /api/admin/plugins/{slug}/versions/{version}/release/
|
||||
|
||||
# Push update to installed sites
|
||||
POST /api/admin/plugins/{slug}/versions/{version}/push-update/
|
||||
# This sets status to 'update_ready' and notifies all installations
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. WordPress Plugin Update Mechanism
|
||||
|
||||
### 4.1 - How WordPress Auto-Updates Work
|
||||
|
||||
WordPress checks for plugin updates by calling a filter. Our plugin hooks into this:
|
||||
|
||||
```php
|
||||
// In igny8-wp-bridge.php
|
||||
|
||||
class IGNY8_WP_Bridge {
|
||||
|
||||
private $plugin_slug = 'igny8-wp-bridge';
|
||||
private $version = '1.0.0';
|
||||
private $api_url = 'https://app.igny8.com/api/plugins/';
|
||||
|
||||
public function __construct() {
|
||||
// Check for updates
|
||||
add_filter('pre_set_site_transient_update_plugins', [$this, 'check_for_updates']);
|
||||
add_filter('plugins_api', [$this, 'plugin_info'], 10, 3);
|
||||
}
|
||||
|
||||
public function check_for_updates($transient) {
|
||||
if (empty($transient->checked)) {
|
||||
return $transient;
|
||||
}
|
||||
|
||||
// Call IGNY8 API to check for updates
|
||||
$response = wp_remote_get($this->api_url . $this->plugin_slug . '/check-update/', [
|
||||
'timeout' => 10,
|
||||
'headers' => [
|
||||
'X-IGNY8-Site-ID' => get_option('igny8_site_id'),
|
||||
'X-IGNY8-API-Key' => get_option('igny8_api_key'),
|
||||
],
|
||||
'body' => [
|
||||
'current_version' => $this->version,
|
||||
]
|
||||
]);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
return $transient;
|
||||
}
|
||||
|
||||
$data = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if (!empty($data['update_available'])) {
|
||||
$plugin_file = $this->plugin_slug . '/' . $this->plugin_slug . '.php';
|
||||
|
||||
$transient->response[$plugin_file] = (object) [
|
||||
'slug' => $this->plugin_slug,
|
||||
'new_version' => $data['latest_version'],
|
||||
'package' => $data['download_url'],
|
||||
'url' => $data['info_url'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
return $transient;
|
||||
}
|
||||
|
||||
public function plugin_info($result, $action, $args) {
|
||||
if ($action !== 'plugin_information' || $args->slug !== $this->plugin_slug) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Fetch plugin info from IGNY8 API
|
||||
$response = wp_remote_get($this->api_url . $this->plugin_slug . '/info/');
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$data = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
return (object) [
|
||||
'name' => $data['name'],
|
||||
'slug' => $this->plugin_slug,
|
||||
'version' => $data['version'],
|
||||
'author' => 'IGNY8',
|
||||
'homepage' => 'https://igny8.com',
|
||||
'sections' => [
|
||||
'description' => $data['description'],
|
||||
'changelog' => $data['changelog'],
|
||||
],
|
||||
'download_link' => $data['download_url'],
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 - Update Flow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Plugin Update Flow │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. Admin marks version as "Update Ready" in IGNY8 Dashboard │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 2. IGNY8 API returns update_available=true for check-update calls │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 3. WordPress cron checks for updates (or user clicks "Check Now") │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 4. WP Plugin calls IGNY8 API → Gets new version info │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 5. WordPress shows "Update Available" in Plugins page │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 6. User clicks Update (or auto-update if enabled) │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 7. WordPress downloads ZIP from IGNY8 and installs │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 8. Plugin activation hook reports new version to IGNY8 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Initial Download (ZIP Generation)
|
||||
|
||||
### 5.1 - Build Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# scripts/build-wp-plugin.sh
|
||||
|
||||
VERSION=$1
|
||||
PLUGIN_NAME="igny8-wp-bridge"
|
||||
SOURCE_DIR="/data/app/igny8/plugins/wordpress/source/${PLUGIN_NAME}"
|
||||
DIST_DIR="/data/app/igny8/plugins/wordpress/dist"
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "Usage: ./build-wp-plugin.sh <version>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create dist directory if not exists
|
||||
mkdir -p "$DIST_DIR"
|
||||
|
||||
# Create temp directory for packaging
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
PACKAGE_DIR="${TEMP_DIR}/${PLUGIN_NAME}"
|
||||
|
||||
# Copy source files
|
||||
cp -r "$SOURCE_DIR" "$PACKAGE_DIR"
|
||||
|
||||
# Update version in main plugin file
|
||||
sed -i "s/Version: .*/Version: ${VERSION}/" "${PACKAGE_DIR}/${PLUGIN_NAME}.php"
|
||||
sed -i "s/\$version = '.*'/\$version = '${VERSION}'/" "${PACKAGE_DIR}/${PLUGIN_NAME}.php"
|
||||
|
||||
# Create ZIP
|
||||
cd "$TEMP_DIR"
|
||||
zip -r "${DIST_DIR}/${PLUGIN_NAME}-v${VERSION}.zip" "$PLUGIN_NAME"
|
||||
|
||||
# Update latest symlink
|
||||
ln -sf "${PLUGIN_NAME}-v${VERSION}.zip" "${DIST_DIR}/${PLUGIN_NAME}-latest.zip"
|
||||
|
||||
# Calculate checksum
|
||||
sha256sum "${DIST_DIR}/${PLUGIN_NAME}-v${VERSION}.zip" > "${DIST_DIR}/${PLUGIN_NAME}-v${VERSION}.sha256"
|
||||
|
||||
# Cleanup
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
||||
echo "Built: ${DIST_DIR}/${PLUGIN_NAME}-v${VERSION}.zip"
|
||||
echo "Checksum: $(cat ${DIST_DIR}/${PLUGIN_NAME}-v${VERSION}.sha256)"
|
||||
```
|
||||
|
||||
### 5.2 - Django Management Command
|
||||
|
||||
```python
|
||||
# backend/igny8_core/plugins/management/commands/build_plugin.py
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
import subprocess
|
||||
import hashlib
|
||||
from plugins.models import Plugin, PluginVersion
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Build and register a new plugin version'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--plugin', type=str, required=True)
|
||||
parser.add_argument('--version', type=str, required=True)
|
||||
parser.add_argument('--changelog', type=str, default='')
|
||||
parser.add_argument('--release', action='store_true')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
plugin = Plugin.objects.get(slug=options['plugin'])
|
||||
version = options['version']
|
||||
|
||||
# Run build script
|
||||
result = subprocess.run(
|
||||
['./scripts/build-wp-plugin.sh', version],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
self.stderr.write(f"Build failed: {result.stderr}")
|
||||
return
|
||||
|
||||
# Calculate version code (1.0.1 -> 101)
|
||||
parts = version.split('.')
|
||||
version_code = int(parts[0]) * 10000 + int(parts[1]) * 100 + int(parts[2])
|
||||
|
||||
# Get file info
|
||||
file_path = f"{plugin.slug}-v{version}.zip"
|
||||
full_path = f"/data/app/igny8/plugins/wordpress/dist/{file_path}"
|
||||
|
||||
with open(full_path, 'rb') as f:
|
||||
checksum = hashlib.sha256(f.read()).hexdigest()
|
||||
|
||||
import os
|
||||
file_size = os.path.getsize(full_path)
|
||||
|
||||
# Create version record
|
||||
plugin_version, created = PluginVersion.objects.update_or_create(
|
||||
plugin=plugin,
|
||||
version=version,
|
||||
defaults={
|
||||
'version_code': version_code,
|
||||
'file_path': file_path,
|
||||
'file_size': file_size,
|
||||
'checksum': checksum,
|
||||
'changelog': options['changelog'],
|
||||
'status': 'released' if options['release'] else 'draft',
|
||||
}
|
||||
)
|
||||
|
||||
action = 'Created' if created else 'Updated'
|
||||
self.stdout.write(f"{action} version {version} for {plugin.name}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Frontend Integration
|
||||
|
||||
### 6.1 - Download Button Component
|
||||
|
||||
```tsx
|
||||
// frontend/src/components/sites/PluginDownloadSection.tsx
|
||||
|
||||
import { Button } from '@/components/ui';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { api } from '@/services/api';
|
||||
|
||||
interface PluginInfo {
|
||||
name: string;
|
||||
slug: string;
|
||||
version: string;
|
||||
download_url: string;
|
||||
file_size: number;
|
||||
platform: string;
|
||||
}
|
||||
|
||||
export function PluginDownloadSection({ platform = 'wordpress' }: { platform: string }) {
|
||||
const { data: plugin, isLoading } = useQuery<PluginInfo>({
|
||||
queryKey: ['plugin', platform],
|
||||
queryFn: () => api.get(`/plugins/${platform}/latest/`).then(r => r.data),
|
||||
});
|
||||
|
||||
const handleDownload = async () => {
|
||||
if (!plugin) return;
|
||||
|
||||
// Track download
|
||||
await api.post(`/plugins/${plugin.slug}/track-download/`);
|
||||
|
||||
// Trigger download
|
||||
window.location.href = plugin.download_url;
|
||||
};
|
||||
|
||||
if (isLoading) return <div>Loading...</div>;
|
||||
if (!plugin) return null;
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="p-3 bg-green-50 rounded-lg">
|
||||
<DownloadIcon className="w-6 h-6 text-green-600" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="font-semibold text-gray-900">{plugin.name}</h3>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
Download and install the plugin on your {platform === 'wordpress' ? 'WordPress' : platform} site
|
||||
</p>
|
||||
<div className="flex items-center gap-4 mt-2 text-xs text-gray-400">
|
||||
<span>Version {plugin.version}</span>
|
||||
<span>{formatFileSize(plugin.file_size)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<Button onClick={handleDownload} variant="outline">
|
||||
<DownloadIcon className="w-4 h-4 mr-2" />
|
||||
Download Plugin
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 - Admin Plugin Management Page (Future)
|
||||
|
||||
```
|
||||
/admin/plugins # List all plugins
|
||||
/admin/plugins/{slug} # Plugin details + versions
|
||||
/admin/plugins/{slug}/versions # Version history
|
||||
/admin/plugins/{slug}/upload # Upload new version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Security Considerations
|
||||
|
||||
### 7.1 - Download Security
|
||||
|
||||
| Measure | Implementation |
|
||||
|---------|----------------|
|
||||
| Signed URLs | Time-limited download URLs (15 min expiry) |
|
||||
| Checksum | SHA256 verification in WP plugin |
|
||||
| API Key Required | Downloads require valid site API key |
|
||||
| Rate Limiting | Max 10 downloads/hour per site |
|
||||
|
||||
### 7.2 - Update Security
|
||||
|
||||
| Measure | Implementation |
|
||||
|---------|----------------|
|
||||
| HTTPS Only | All API calls over TLS |
|
||||
| Version Signing | Checksum comparison before install |
|
||||
| Rollback | Keep last 3 versions for emergency rollback |
|
||||
| Staged Rollout | Option to release to % of installs first |
|
||||
|
||||
---
|
||||
|
||||
## 8. Implementation Phases
|
||||
|
||||
### Phase 1: Basic Infrastructure (Week 1)
|
||||
- [ ] Create `/plugins/` directory structure
|
||||
- [ ] Create Django `plugins` app with models
|
||||
- [ ] Migrate existing WP plugin to `/plugins/wordpress/source/`
|
||||
- [ ] Create build script for ZIP generation
|
||||
- [ ] Implement download API endpoint
|
||||
|
||||
### Phase 2: Frontend Integration (Week 1)
|
||||
- [ ] Update Site Settings > Integrations download button
|
||||
- [ ] Create plugin info API endpoint
|
||||
- [ ] Add version display to download section
|
||||
|
||||
### Phase 3: Update System (Week 2)
|
||||
- [ ] Implement check-update API
|
||||
- [ ] Add update hooks to WP plugin
|
||||
- [ ] Create PluginInstallation tracking
|
||||
- [ ] Build admin version management UI
|
||||
|
||||
### Phase 4: Advanced Features (Week 3)
|
||||
- [ ] Signed download URLs
|
||||
- [ ] Version rollback support
|
||||
- [ ] Update notifications in IGNY8 dashboard
|
||||
- [ ] Staged rollout percentage
|
||||
|
||||
### Phase 5: Additional Platforms (Future)
|
||||
- [ ] Shopify plugin structure
|
||||
- [ ] Custom site SDK/package
|
||||
- [ ] Platform-specific update mechanisms
|
||||
|
||||
---
|
||||
|
||||
## 9. File Serving Configuration
|
||||
|
||||
### 9.1 - Nginx Configuration
|
||||
|
||||
```nginx
|
||||
# Add to nginx config for igny8
|
||||
|
||||
# Serve plugin downloads
|
||||
location /plugins/download/ {
|
||||
alias /data/app/igny8/plugins/;
|
||||
|
||||
# Only allow ZIP files
|
||||
location ~ \.zip$ {
|
||||
add_header Content-Disposition 'attachment';
|
||||
add_header X-Content-Type-Options 'nosniff';
|
||||
}
|
||||
|
||||
# Deny access to source files
|
||||
location ~ /source/ {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 9.2 - Django URL (Proxied Downloads)
|
||||
|
||||
```python
|
||||
# Better approach: Django handles download with auth
|
||||
|
||||
# backend/igny8_core/plugins/views.py
|
||||
|
||||
from django.http import FileResponse
|
||||
from rest_framework.decorators import api_view, permission_classes
|
||||
from rest_framework.permissions import AllowAny
|
||||
|
||||
@api_view(['GET'])
|
||||
@permission_classes([AllowAny])
|
||||
def download_plugin(request, slug):
|
||||
plugin = get_object_or_404(Plugin, slug=slug, is_active=True)
|
||||
latest = plugin.versions.filter(status__in=['released', 'update_ready']).first()
|
||||
|
||||
if not latest:
|
||||
return Response({'error': 'No version available'}, status=404)
|
||||
|
||||
file_path = f'/data/app/igny8/plugins/{plugin.platform}/dist/{latest.file_path}'
|
||||
|
||||
response = FileResponse(
|
||||
open(file_path, 'rb'),
|
||||
content_type='application/zip'
|
||||
)
|
||||
response['Content-Disposition'] = f'attachment; filename="{latest.file_path}"'
|
||||
return response
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Monitoring & Analytics
|
||||
|
||||
### 10.1 - Metrics to Track
|
||||
|
||||
| Metric | Purpose |
|
||||
|--------|---------|
|
||||
| Downloads per version | Adoption rate |
|
||||
| Active installations | Current reach |
|
||||
| Update success rate | Quality indicator |
|
||||
| Version distribution | Upgrade urgency |
|
||||
| Health check failures | Support needs |
|
||||
|
||||
### 10.2 - Admin Dashboard Widgets
|
||||
|
||||
- **Plugin Downloads** - Daily/weekly download counts
|
||||
- **Version Distribution** - Pie chart of installed versions
|
||||
- **Update Adoption** - Time to 80% adoption after release
|
||||
- **Installation Health** - Sites with outdated/failing plugins
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Commands
|
||||
|
||||
```bash
|
||||
# Build new version
|
||||
python manage.py build_plugin --plugin=igny8-wp-bridge --version=1.0.1 --changelog="Bug fixes"
|
||||
|
||||
# Release version (make downloadable)
|
||||
python manage.py release_plugin --plugin=igny8-wp-bridge --version=1.0.1
|
||||
|
||||
# Push update to sites
|
||||
python manage.py push_plugin_update --plugin=igny8-wp-bridge --version=1.0.1
|
||||
```
|
||||
|
||||
### API Quick Reference
|
||||
|
||||
```
|
||||
GET /api/plugins/{slug}/download/ # Download latest ZIP
|
||||
GET /api/plugins/{slug}/check-update/ # Check for updates
|
||||
GET /api/plugins/{slug}/info/ # Plugin metadata
|
||||
POST /api/plugins/{slug}/register/ # Register installation
|
||||
POST /api/plugins/{slug}/health-check/ # Report status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Document Owner:** IGNY8 Team
|
||||
**Next Review:** After Phase 1 implementation
|
||||
Reference in New Issue
Block a user