Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aff7263afd |
@@ -0,0 +1,35 @@
|
|||||||
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
let containerStatus = '';
|
||||||
|
let error = '';
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/container/status');
|
||||||
|
const data = await response.json();
|
||||||
|
containerStatus = data.status;
|
||||||
|
} catch (err) {
|
||||||
|
error = 'Failed to fetch container status';
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container-status">
|
||||||
|
{#if error}
|
||||||
|
<p class="error">{error}</p>
|
||||||
|
{:else}
|
||||||
|
<p>Container Status: {containerStatus}</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container-status {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# OpenWebUI Container Management Plugin
|
||||||
|
|
||||||
|
This plugin adds container management capabilities to OpenWebUI, allowing you to monitor, update, and restart the container directly from the web interface.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- View container status and version
|
||||||
|
- Update container to the latest version
|
||||||
|
- Restart container when needed
|
||||||
|
- User-friendly interface integrated into OpenWebUI settings
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Installing in Docker-based OpenWebUI
|
||||||
|
|
||||||
|
1. If your OpenWebUI is running in Docker, mount the plugin directory into the container by adding this volume to your docker-compose.yml:
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
openwebui:
|
||||||
|
volumes:
|
||||||
|
- ./custom:/app/custom # Mount the plugin directory
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Copy the `custom` directory to your OpenWebUI installation directory (where your docker-compose.yml is located)
|
||||||
|
|
||||||
|
3. Restart your OpenWebUI container:
|
||||||
|
```bash
|
||||||
|
docker-compose restart openwebui
|
||||||
|
```
|
||||||
|
|
||||||
|
The plugin will be automatically detected and loaded by OpenWebUI when it starts.
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
The plugin requires the following Python packages which are already included in the OpenWebUI Docker image:
|
||||||
|
- fastapi
|
||||||
|
- docker-py
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Navigate to the OpenWebUI settings page
|
||||||
|
2. Look for the "Container Management" section
|
||||||
|
3. You can:
|
||||||
|
- View the current container status and version
|
||||||
|
- Click "Update Container" to update to the latest version
|
||||||
|
- Click "Restart Container" to restart the container
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
The plugin exposes the following API endpoints:
|
||||||
|
|
||||||
|
- `GET /api/container/status` - Get container status and version
|
||||||
|
- `POST /api/container/update` - Update the container
|
||||||
|
- `POST /api/container/restart` - Restart the container
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
To modify or extend the plugin:
|
||||||
|
|
||||||
|
1. Frontend components are in `custom/components/`
|
||||||
|
2. Backend API routes are in `custom/api/`
|
||||||
|
3. Container management logic is in `custom/container_manager.py`
|
||||||
|
4. Plugin registration is handled in `custom/plugin.py`
|
||||||
|
|
||||||
|
### Development Workflow
|
||||||
|
|
||||||
|
1. Make changes to the plugin code
|
||||||
|
2. The changes will be reflected immediately in the OpenWebUI container due to the volume mount
|
||||||
|
3. If you modify Python files, you'll need to restart the OpenWebUI container:
|
||||||
|
```bash
|
||||||
|
docker-compose restart openwebui
|
||||||
|
```
|
||||||
|
4. Frontend changes (Svelte components) will be hot-reloaded automatically
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Feel free to submit issues and pull requests to improve the plugin.
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
from fastapi import APIRouter, HTTPException
|
||||||
|
from typing import Dict
|
||||||
|
from ..container_manager import ContainerManager
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
container_manager = ContainerManager()
|
||||||
|
|
||||||
|
@router.get("/status")
|
||||||
|
async def get_container_status() -> Dict:
|
||||||
|
"""Get the current status and version of the container."""
|
||||||
|
try:
|
||||||
|
status = container_manager.get_status()
|
||||||
|
version = container_manager.get_version()
|
||||||
|
return {
|
||||||
|
"status": status,
|
||||||
|
"version": version
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
@router.post("/update")
|
||||||
|
async def update_container() -> Dict:
|
||||||
|
"""Update the container to the latest version."""
|
||||||
|
try:
|
||||||
|
success = container_manager.update()
|
||||||
|
return {
|
||||||
|
"success": success,
|
||||||
|
"message": "Container updated successfully" if success else "Update failed"
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
@router.post("/restart")
|
||||||
|
async def restart_container() -> Dict:
|
||||||
|
"""Restart the container."""
|
||||||
|
try:
|
||||||
|
success = container_manager.restart()
|
||||||
|
return {
|
||||||
|
"success": success,
|
||||||
|
"message": "Container restarted successfully" if success else "Restart failed"
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
{/* ContainerSettings.svelte */}
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { Button } from '$lib/components/ui/button';
|
||||||
|
import { Input } from '$lib/components/ui/input';
|
||||||
|
import { Label } from '$lib/components/ui/label';
|
||||||
|
import { Card } from '$lib/components/ui/card';
|
||||||
|
import { toast } from '$lib/components/ui/toaster';
|
||||||
|
|
||||||
|
let containerStatus = '';
|
||||||
|
let containerVersion = '';
|
||||||
|
let isUpdating = false;
|
||||||
|
let isRestarting = false;
|
||||||
|
|
||||||
|
async function fetchContainerStatus() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/container/status');
|
||||||
|
const data = await response.json();
|
||||||
|
containerStatus = data.status;
|
||||||
|
containerVersion = data.version;
|
||||||
|
} catch (error) {
|
||||||
|
toast({
|
||||||
|
title: 'Error',
|
||||||
|
description: 'Failed to fetch container status',
|
||||||
|
variant: 'destructive'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleUpdate() {
|
||||||
|
isUpdating = true;
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/container/update', {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.success) {
|
||||||
|
toast({
|
||||||
|
title: 'Success',
|
||||||
|
description: 'Container updated successfully'
|
||||||
|
});
|
||||||
|
await fetchContainerStatus();
|
||||||
|
} else {
|
||||||
|
throw new Error(data.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast({
|
||||||
|
title: 'Error',
|
||||||
|
description: 'Failed to update container',
|
||||||
|
variant: 'destructive'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
isUpdating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleRestart() {
|
||||||
|
isRestarting = true;
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/container/restart', {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.success) {
|
||||||
|
toast({
|
||||||
|
title: 'Success',
|
||||||
|
description: 'Container restarted successfully'
|
||||||
|
});
|
||||||
|
await fetchContainerStatus();
|
||||||
|
} else {
|
||||||
|
throw new Error(data.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast({
|
||||||
|
title: 'Error',
|
||||||
|
description: 'Failed to restart container',
|
||||||
|
variant: 'destructive'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
isRestarting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
fetchContainerStatus();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Card class="p-6">
|
||||||
|
<h2 class="text-2xl font-bold mb-4">Container Management</h2>
|
||||||
|
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<Label>Status</Label>
|
||||||
|
<Input value={containerStatus} readonly />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<Label>Version</Label>
|
||||||
|
<Input value={containerVersion} readonly />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex gap-4 mt-4">
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
disabled={isUpdating}
|
||||||
|
on:click={handleUpdate}
|
||||||
|
>
|
||||||
|
{isUpdating ? 'Updating...' : 'Update Container'}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
disabled={isRestarting}
|
||||||
|
on:click={handleRestart}
|
||||||
|
>
|
||||||
|
{isRestarting ? 'Restarting...' : 'Restart Container'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
"""
|
||||||
|
type: action
|
||||||
|
name: Container Manager
|
||||||
|
description: Manage chat.Sami container updates
|
||||||
|
version: 1.0.0
|
||||||
|
author: chat.Sami
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
class Action:
|
||||||
|
def __init__(self):
|
||||||
|
self.actions = [
|
||||||
|
{
|
||||||
|
"id": "update_container",
|
||||||
|
"name": "Update Container",
|
||||||
|
"description": "Update chat.Sami to the latest version"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "container_status",
|
||||||
|
"name": "Container Status",
|
||||||
|
"description": "Get current container status and version"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "check_updates",
|
||||||
|
"name": "Check Updates",
|
||||||
|
"description": "Check for available updates"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
def execute(self, action_id: str, params: Dict[str, Any] = None) -> Dict[str, Any]:
|
||||||
|
if action_id == "update_container":
|
||||||
|
return self._update_container()
|
||||||
|
elif action_id == "container_status":
|
||||||
|
return self._get_container_status()
|
||||||
|
elif action_id == "check_updates":
|
||||||
|
return self._check_updates()
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown action: {action_id}")
|
||||||
|
|
||||||
|
def _get_container_config(self) -> Dict[str, Any]:
|
||||||
|
"""Get current container configuration"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["docker", "inspect", "chat-sami"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
container_info = json.loads(result.stdout)[0]
|
||||||
|
|
||||||
|
# Extract current configuration
|
||||||
|
config = {
|
||||||
|
"port": None,
|
||||||
|
"volumes": [],
|
||||||
|
"env": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get port mapping
|
||||||
|
ports = container_info["HostConfig"]["PortBindings"]
|
||||||
|
if "8080/tcp" in ports:
|
||||||
|
port_info = ports["8080/tcp"][0]
|
||||||
|
config["port"] = port_info["HostPort"]
|
||||||
|
|
||||||
|
# Get volume mappings
|
||||||
|
mounts = container_info["HostConfig"]["Mounts"]
|
||||||
|
for mount in mounts:
|
||||||
|
config["volumes"].append({
|
||||||
|
"source": mount["Source"],
|
||||||
|
"target": mount["Target"]
|
||||||
|
})
|
||||||
|
|
||||||
|
# Get environment variables
|
||||||
|
env_list = container_info["Config"]["Env"]
|
||||||
|
for env in env_list:
|
||||||
|
if "=" in env:
|
||||||
|
key, value = env.split("=", 1)
|
||||||
|
config["env"][key] = value
|
||||||
|
|
||||||
|
return config
|
||||||
|
except Exception as e:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _get_current_version(self) -> str:
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["docker", "inspect", "chat-sami"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
container_info = json.loads(result.stdout)[0]
|
||||||
|
image_tag = container_info["Config"]["Image"].split(":")[-1]
|
||||||
|
return image_tag
|
||||||
|
except:
|
||||||
|
return "unknown"
|
||||||
|
|
||||||
|
def _get_latest_version(self) -> str:
|
||||||
|
try:
|
||||||
|
response = requests.get(
|
||||||
|
"https://api.github.com/repos/open-webui/open-webui/releases/latest",
|
||||||
|
timeout=5
|
||||||
|
)
|
||||||
|
if response.status_code == 200:
|
||||||
|
return response.json()["tag_name"].lstrip('v')
|
||||||
|
return "unknown"
|
||||||
|
except:
|
||||||
|
return "unknown"
|
||||||
|
|
||||||
|
def _check_updates(self) -> Dict[str, Any]:
|
||||||
|
current = self._get_current_version()
|
||||||
|
latest = self._get_latest_version()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"data": {
|
||||||
|
"current_version": current,
|
||||||
|
"latest_version": latest,
|
||||||
|
"update_available": latest != "unknown" and current != latest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def _update_container(self) -> Dict[str, Any]:
|
||||||
|
try:
|
||||||
|
# Get current container configuration
|
||||||
|
config = self._get_container_config()
|
||||||
|
if not config:
|
||||||
|
raise Exception("Failed to get current container configuration")
|
||||||
|
|
||||||
|
# Pull the latest image
|
||||||
|
subprocess.run(
|
||||||
|
["docker", "pull", "ghcr.io/open-webui/open-webui:main"],
|
||||||
|
check=True,
|
||||||
|
capture_output=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Stop the current container
|
||||||
|
subprocess.run(
|
||||||
|
["docker", "stop", "chat-sami"],
|
||||||
|
check=True,
|
||||||
|
capture_output=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Remove the old container but keep the volumes
|
||||||
|
subprocess.run(
|
||||||
|
["docker", "rm", "chat-sami"],
|
||||||
|
check=True,
|
||||||
|
capture_output=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create new container with same configuration
|
||||||
|
cmd = ["docker", "run", "-d", "--name", "chat-sami"]
|
||||||
|
|
||||||
|
# Add port mapping
|
||||||
|
if config["port"]:
|
||||||
|
cmd.extend(["-p", f"{config['port']}:8080"])
|
||||||
|
|
||||||
|
# Add volume mappings
|
||||||
|
for volume in config["volumes"]:
|
||||||
|
cmd.extend(["-v", f"{volume['source']}:{volume['target']}"])
|
||||||
|
|
||||||
|
# Add environment variables
|
||||||
|
for key, value in config["env"].items():
|
||||||
|
cmd.extend(["-e", f"{key}={value}"])
|
||||||
|
|
||||||
|
# Add image name
|
||||||
|
cmd.append("ghcr.io/open-webui/open-webui:main")
|
||||||
|
|
||||||
|
# Run the new container
|
||||||
|
subprocess.run(cmd, check=True, capture_output=True)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"message": "Updated to latest version. The page will refresh in a few moments."
|
||||||
|
}
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": f"Update failed: {e.stderr.decode() if e.stderr else str(e)}"
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": f"Error during update: {str(e)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_container_status(self) -> Dict[str, Any]:
|
||||||
|
try:
|
||||||
|
# Get container info
|
||||||
|
result = subprocess.run(
|
||||||
|
["docker", "inspect", "chat-sami"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
container_info = json.loads(result.stdout)[0]
|
||||||
|
|
||||||
|
# Get update status
|
||||||
|
update_info = self._check_updates()
|
||||||
|
current_version = update_info["data"]["current_version"]
|
||||||
|
latest_version = update_info["data"]["latest_version"]
|
||||||
|
update_available = update_info["data"]["update_available"]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"data": {
|
||||||
|
"status": container_info["State"]["Status"],
|
||||||
|
"health": container_info["State"]["Health"]["Status"] if "Health" in container_info["State"] else "N/A",
|
||||||
|
"current_version": current_version,
|
||||||
|
"latest_version": latest_version,
|
||||||
|
"update_available": update_available,
|
||||||
|
"created": container_info["Created"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": f"Failed to get container status: {e.stderr.decode() if e.stderr else str(e)}"
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": f"Error: {str(e)}"
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
"""
|
||||||
|
type: settings
|
||||||
|
name: Container Settings
|
||||||
|
description: Manage chat.Sami container settings
|
||||||
|
version: 1.0.0
|
||||||
|
author: chat.Sami
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Dict, Any
|
||||||
|
import json
|
||||||
|
from fastapi import APIRouter
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
class ContainerSettings(BaseModel):
|
||||||
|
"""Container management settings."""
|
||||||
|
enabled: bool = True
|
||||||
|
|
||||||
|
@router.get("/settings/container")
|
||||||
|
async def get_container_settings() -> Dict[str, Any]:
|
||||||
|
"""Get container management settings."""
|
||||||
|
return {
|
||||||
|
"id": "container",
|
||||||
|
"title": "Container Management",
|
||||||
|
"description": "Manage the chat.Sami container",
|
||||||
|
"component": "ContainerStatus",
|
||||||
|
"settings": ContainerSettings().dict()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Settings:
|
||||||
|
def __init__(self):
|
||||||
|
self.settings = {
|
||||||
|
"container": {
|
||||||
|
"title": "Container Management",
|
||||||
|
"description": "Manage your chat.Sami container",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"type": "custom",
|
||||||
|
"component": "ContainerStatus",
|
||||||
|
"action": "container_status",
|
||||||
|
"refresh_interval": 30, # Refresh every 30 seconds
|
||||||
|
"title": "Container Status"
|
||||||
|
},
|
||||||
|
"update": {
|
||||||
|
"type": "button",
|
||||||
|
"title": "Update Container",
|
||||||
|
"description": "Update chat.Sami to the latest version",
|
||||||
|
"action": "update_container",
|
||||||
|
"buttonText": "Update Now",
|
||||||
|
"confirmText": "Are you sure you want to update the container? The service will restart briefly."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_settings(self) -> Dict[str, Any]:
|
||||||
|
return self.settings
|
||||||
|
|
||||||
|
def update_settings(self, settings: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
# This is a read-only settings panel
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"message": "Settings updated"
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
export async function executeAction<T>(actionId: string, params?: Record<string, unknown>): Promise<T> {
|
||||||
|
// This is a mock implementation for testing
|
||||||
|
const mockResponses = {
|
||||||
|
container_status: {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
status: "running",
|
||||||
|
health: "healthy",
|
||||||
|
current_version: "1.0.0",
|
||||||
|
latest_version: "1.1.0",
|
||||||
|
update_available: true,
|
||||||
|
created: new Date().toISOString()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
update_container: {
|
||||||
|
success: true,
|
||||||
|
message: "Update initiated"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(mockResponses[actionId as keyof typeof mockResponses] as T);
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
from .api.container_routes import router as container_router
|
||||||
|
|
||||||
|
def register_plugin(app: FastAPI) -> None:
|
||||||
|
"""Register the container management plugin with OpenWebUI."""
|
||||||
|
# Register API routes
|
||||||
|
app.include_router(
|
||||||
|
container_router,
|
||||||
|
prefix="/api/container",
|
||||||
|
tags=["container"]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Register frontend components
|
||||||
|
app.state.plugin_components = app.state.plugin_components or {}
|
||||||
|
app.state.plugin_components["container_settings"] = {
|
||||||
|
"name": "ContainerSettings",
|
||||||
|
"path": "custom/components/ContainerSettings.svelte",
|
||||||
|
"settings": {
|
||||||
|
"title": "Container Management",
|
||||||
|
"icon": "container",
|
||||||
|
"description": "Manage container updates and status"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export interface ContainerStatus {
|
||||||
|
status: string;
|
||||||
|
health: string;
|
||||||
|
current_version: string;
|
||||||
|
latest_version: string;
|
||||||
|
update_available: boolean;
|
||||||
|
created: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContainerResponse {
|
||||||
|
success: boolean;
|
||||||
|
data?: ContainerStatus;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user