516 lines
18 KiB
Vue
516 lines
18 KiB
Vue
<script setup>
|
|
definePageMeta({
|
|
title: "Upload Plugin",
|
|
middleware: ["auth"],
|
|
requiresAuth: true,
|
|
});
|
|
|
|
const nuxtApp = useNuxtApp();
|
|
|
|
// Upload states
|
|
const isUploading = ref(false);
|
|
const uploadProgress = ref(0);
|
|
const uploadedFile = ref(null);
|
|
const installationLog = ref([]);
|
|
|
|
// Form data
|
|
const uploadForm = ref({
|
|
file: null,
|
|
agreeToTerms: false,
|
|
overwriteExisting: false
|
|
});
|
|
|
|
// Upload methods
|
|
const handleFileUpload = (files) => {
|
|
if (files && files.length > 0) {
|
|
uploadedFile.value = files[0];
|
|
}
|
|
};
|
|
|
|
const simulateUpload = async () => {
|
|
if (!uploadedFile.value || !uploadForm.value.agreeToTerms) {
|
|
nuxtApp.$swal.fire({
|
|
title: "Error",
|
|
text: "Please select a file and agree to terms",
|
|
icon: "error",
|
|
});
|
|
return;
|
|
}
|
|
|
|
isUploading.value = true;
|
|
uploadProgress.value = 0;
|
|
installationLog.value = [];
|
|
|
|
// Simulate upload progress
|
|
const steps = [
|
|
{ progress: 20, message: "Uploading plugin package..." },
|
|
{ progress: 40, message: "Validating plugin manifest..." },
|
|
{ progress: 60, message: "Checking dependencies..." },
|
|
{ progress: 80, message: "Installing plugin files..." },
|
|
{ progress: 100, message: "Plugin installed successfully!" }
|
|
];
|
|
|
|
for (const step of steps) {
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
uploadProgress.value = step.progress;
|
|
installationLog.value.push({
|
|
message: step.message,
|
|
timestamp: new Date().toLocaleTimeString(),
|
|
type: step.progress === 100 ? 'success' : 'info'
|
|
});
|
|
}
|
|
|
|
isUploading.value = false;
|
|
|
|
nuxtApp.$swal.fire({
|
|
title: "Success",
|
|
text: "Plugin uploaded and installed successfully!",
|
|
icon: "success",
|
|
timer: 3000,
|
|
showConfirmButton: false,
|
|
}).then(() => {
|
|
navigateTo('/devtool/plugin-manager/installed');
|
|
});
|
|
};
|
|
|
|
const resetUpload = () => {
|
|
uploadedFile.value = null;
|
|
uploadProgress.value = 0;
|
|
isUploading.value = false;
|
|
installationLog.value = [];
|
|
uploadForm.value = {
|
|
file: null,
|
|
agreeToTerms: false,
|
|
overwriteExisting: false
|
|
};
|
|
};
|
|
|
|
const formatFileSize = (bytes) => {
|
|
if (bytes === 0) return '0 Bytes';
|
|
const k = 1024;
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
};
|
|
|
|
// app.config.js example code
|
|
const appConfigExample = ref(`export default {
|
|
// Application Identity
|
|
name: 'notification-management',
|
|
version: '1.2.0',
|
|
displayName: 'Notification Management System',
|
|
description: 'Complete notification system with templates, scheduling, and analytics',
|
|
|
|
// Authentik Integration
|
|
authentik: {
|
|
applicationSlug: 'notification-management',
|
|
requiredScopes: ['notifications:read', 'notifications:write', 'notifications:admin'],
|
|
groups: ['notification-users', 'notification-admins']
|
|
},
|
|
|
|
// CORRAD Menu Integration
|
|
menuStructure: {
|
|
title: 'Notifications',
|
|
icon: 'bell',
|
|
permission: 'notifications:read',
|
|
order: 100,
|
|
children: [
|
|
{
|
|
title: 'Dashboard',
|
|
route: '/notifications/dashboard',
|
|
icon: 'dashboard',
|
|
permission: 'notifications:read'
|
|
},
|
|
{
|
|
title: 'Send Notification',
|
|
route: '/notifications/send',
|
|
icon: 'send',
|
|
permission: 'notifications:write'
|
|
},
|
|
{
|
|
title: 'Templates',
|
|
route: '/notifications/templates',
|
|
icon: 'template',
|
|
permission: 'notifications:write'
|
|
},
|
|
{
|
|
title: 'History',
|
|
route: '/notifications/history',
|
|
icon: 'history',
|
|
permission: 'notifications:read'
|
|
},
|
|
{
|
|
title: 'Settings',
|
|
route: '/notifications/settings',
|
|
icon: 'settings',
|
|
permission: 'notifications:admin'
|
|
},
|
|
{
|
|
title: 'Reports',
|
|
route: '/notifications/reports',
|
|
icon: 'chart',
|
|
permission: 'notifications:admin'
|
|
}
|
|
]
|
|
},
|
|
|
|
// Database Schema
|
|
migrations: [
|
|
'migrations/001_create_notifications.sql',
|
|
'migrations/002_create_templates.sql',
|
|
'migrations/003_create_schedules.sql'
|
|
],
|
|
|
|
// API Endpoints
|
|
apiRoutes: [
|
|
{
|
|
path: '/api/notifications',
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE'],
|
|
permissions: {
|
|
'GET': 'notifications:read',
|
|
'POST': 'notifications:write',
|
|
'PUT': 'notifications:write',
|
|
'DELETE': 'notifications:admin'
|
|
}
|
|
},
|
|
{
|
|
path: '/api/notifications/templates',
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE'],
|
|
permissions: {
|
|
'GET': 'notifications:read',
|
|
'POST': 'notifications:write',
|
|
'PUT': 'notifications:write',
|
|
'DELETE': 'notifications:admin'
|
|
}
|
|
}
|
|
],
|
|
|
|
// Dependencies
|
|
dependencies: {
|
|
corrad: '^2.0.0',
|
|
authentik: '^2023.10.0'
|
|
},
|
|
|
|
// Installation hooks
|
|
hooks: {
|
|
preInstall: 'scripts/pre-install.js',
|
|
postInstall: 'scripts/post-install.js',
|
|
preUninstall: 'scripts/pre-uninstall.js'
|
|
}
|
|
}`);
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<LayoutsBreadcrumb />
|
|
|
|
<!-- Header -->
|
|
<rs-card class="mb-6">
|
|
<template #body>
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-gray-900 mb-2">Upload Plugin</h1>
|
|
<p class="text-gray-600">
|
|
Install custom plugin packages to extend your application
|
|
</p>
|
|
</div>
|
|
<div class="hidden md:block">
|
|
<Icon name="mdi:cloud-upload" size="64" class="text-primary/20" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Security Warning -->
|
|
<rs-card class="mb-6">
|
|
<template #body>
|
|
<div class="flex items-start space-x-3">
|
|
<Icon name="mdi:security" class="text-orange-500 mt-1" size="20" />
|
|
<div>
|
|
<h3 class="font-medium text-orange-800 mb-2">Plugin Installation Guidelines</h3>
|
|
<p class="text-orange-700 text-sm mb-3">
|
|
Upload plugin packages for CORRAD+ applications. Each plugin should include app.config.js and proper folder structure.
|
|
Supported plugins: RBAC, Notification Management, Business Process Maker, Queue Management, Report Management, Audit Trail, and EDMS.
|
|
</p>
|
|
<div class="grid grid-cols-3 md:grid-cols-7 gap-2">
|
|
<div class="bg-green-100 rounded px-2 py-1 text-xs text-center">
|
|
<Icon name="mdi:shield-account" class="text-green-600 mb-1" size="12" />
|
|
<div class="text-green-800">RBAC</div>
|
|
</div>
|
|
<div class="bg-orange-100 rounded px-2 py-1 text-xs text-center">
|
|
<Icon name="mdi:bell-ring" class="text-orange-600 mb-1" size="12" />
|
|
<div class="text-orange-800">Notify</div>
|
|
</div>
|
|
<div class="bg-blue-100 rounded px-2 py-1 text-xs text-center">
|
|
<Icon name="mdi:flowchart" class="text-blue-600 mb-1" size="12" />
|
|
<div class="text-blue-800">BPM</div>
|
|
</div>
|
|
<div class="bg-indigo-100 rounded px-2 py-1 text-xs text-center">
|
|
<Icon name="mdi:format-list-numbered" class="text-indigo-600 mb-1" size="12" />
|
|
<div class="text-indigo-800">Queue</div>
|
|
</div>
|
|
<div class="bg-blue-100 rounded px-2 py-1 text-xs text-center">
|
|
<Icon name="mdi:chart-line" class="text-blue-600 mb-1" size="12" />
|
|
<div class="text-blue-800">Reports</div>
|
|
</div>
|
|
<div class="bg-purple-100 rounded px-2 py-1 text-xs text-center">
|
|
<Icon name="mdi:file-document-alert" class="text-purple-600 mb-1" size="12" />
|
|
<div class="text-purple-800">Audit</div>
|
|
</div>
|
|
<div class="bg-red-100 rounded px-2 py-1 text-xs text-center">
|
|
<Icon name="mdi:file-document-multiple" class="text-red-600 mb-1" size="12" />
|
|
<div class="text-red-800">EDMS</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<!-- Upload Form -->
|
|
<rs-card>
|
|
<template #header>
|
|
<div class="flex items-center">
|
|
<Icon name="mdi:upload" class="mr-2" />
|
|
Upload Plugin Package
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<FormKit type="form" :actions="false" @submit="simulateUpload">
|
|
<!-- File Upload -->
|
|
<div class="mb-6">
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">
|
|
Plugin Package (.zip)
|
|
</label>
|
|
<div
|
|
class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center hover:border-primary transition-colors"
|
|
:class="{ 'border-primary bg-primary/5': uploadedFile }"
|
|
>
|
|
<div v-if="!uploadedFile">
|
|
<Icon name="mdi:cloud-upload" class="mx-auto text-gray-400 mb-4" size="48" />
|
|
<p class="text-gray-600 mb-2">Drag and drop your plugin file here, or</p>
|
|
<FormKit
|
|
type="file"
|
|
accept=".zip"
|
|
@change="handleFileUpload"
|
|
wrapper-class="inline-block"
|
|
outer-class="mb-0"
|
|
input-class="hidden"
|
|
>
|
|
<template #label>
|
|
<rs-button variant="outline" size="sm">
|
|
Browse Files
|
|
</rs-button>
|
|
</template>
|
|
</FormKit>
|
|
<p class="text-xs text-gray-500 mt-2">Maximum file size: 50MB</p>
|
|
</div>
|
|
<div v-else class="space-y-2">
|
|
<Icon name="mdi:file-check" class="mx-auto text-green-500 mb-2" size="32" />
|
|
<p class="font-medium text-gray-900">{{ uploadedFile.name }}</p>
|
|
<p class="text-sm text-gray-500">{{ formatFileSize(uploadedFile.size) }}</p>
|
|
<rs-button variant="outline" size="sm" @click="resetUpload">
|
|
Choose Different File
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Options -->
|
|
<div class="space-y-4 mb-6">
|
|
<FormKit
|
|
type="checkbox"
|
|
v-model="uploadForm.overwriteExisting"
|
|
label="Overwrite existing plugin if it exists"
|
|
help="This will replace any existing plugin with the same name"
|
|
/>
|
|
|
|
<FormKit
|
|
type="checkbox"
|
|
v-model="uploadForm.agreeToTerms"
|
|
label="I understand that this plugin will be installed as a complete CORRAD+ application"
|
|
validation="required"
|
|
:validation-messages="{ required: 'You must confirm to continue' }"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Upload Button -->
|
|
<div class="flex gap-3">
|
|
<rs-button
|
|
btnType="submit"
|
|
class="flex-1"
|
|
:disabled="!uploadedFile || isUploading"
|
|
>
|
|
<Icon v-if="!isUploading" name="mdi:upload" class="mr-2" size="16" />
|
|
<Icon v-else name="mdi:loading" class="mr-2 animate-spin" size="16" />
|
|
{{ isUploading ? 'Installing...' : 'Upload & Install' }}
|
|
</rs-button>
|
|
<rs-button variant="outline" @click="resetUpload" :disabled="isUploading">
|
|
Reset
|
|
</rs-button>
|
|
</div>
|
|
</FormKit>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Upload Progress & Instructions -->
|
|
<div class="space-y-6">
|
|
<!-- Progress -->
|
|
<rs-card v-if="isUploading || uploadProgress > 0">
|
|
<template #header>
|
|
<div class="flex items-center">
|
|
<Icon name="mdi:progress-upload" class="mr-2" />
|
|
Installation Progress
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="space-y-4">
|
|
<!-- Progress Bar -->
|
|
<div>
|
|
<div class="flex justify-between text-sm mb-1">
|
|
<span>Installing plugin...</span>
|
|
<span>{{ uploadProgress }}%</span>
|
|
</div>
|
|
<div class="w-full bg-gray-200 rounded-full h-2">
|
|
<div
|
|
class="bg-primary h-2 rounded-full transition-all duration-300"
|
|
:style="{ width: uploadProgress + '%' }"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Installation Log -->
|
|
<div class="space-y-2 max-h-40 overflow-y-auto">
|
|
<div
|
|
v-for="(log, index) in installationLog"
|
|
:key="index"
|
|
class="flex items-start space-x-2 text-sm"
|
|
>
|
|
<Icon
|
|
:name="log.type === 'success' ? 'mdi:check-circle' : 'mdi:information'"
|
|
:class="log.type === 'success' ? 'text-green-500' : 'text-blue-500'"
|
|
size="16"
|
|
class="mt-0.5"
|
|
/>
|
|
<div class="flex-1">
|
|
<span class="text-gray-900">{{ log.message }}</span>
|
|
<span class="text-gray-500 ml-2">{{ log.timestamp }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Instructions -->
|
|
<rs-card>
|
|
<template #header>
|
|
<div class="flex items-center">
|
|
<Icon name="mdi:information-outline" class="mr-2" />
|
|
Plugin Requirements
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="space-y-4 text-sm">
|
|
<div>
|
|
<h4 class="font-medium text-gray-900 mb-2">Plugin Structure Requirements</h4>
|
|
<ul class="list-disc list-inside text-gray-600 space-y-1">
|
|
<li>Must be a valid .zip file</li>
|
|
<li>Include app.config.js with complete application metadata</li>
|
|
<li>Authentik integration configuration (scopes, groups, permissions)</li>
|
|
<li>CORRAD menu structure with proper routing and permissions</li>
|
|
<li>Database migrations with SQL files</li>
|
|
<li>API routes with method-specific permissions</li>
|
|
<li>Installation hooks for pre/post install actions</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 class="font-medium text-gray-900 mb-2">app.config.js Example</h4>
|
|
<RsCodeMirror
|
|
:model-value="appConfigExample"
|
|
mode="javascript"
|
|
height="300px"
|
|
:disabled="true"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 class="font-medium text-gray-900 mb-2">Complete Folder Structure</h4>
|
|
<div class="bg-gray-100 p-3 rounded text-xs font-mono">
|
|
<pre>notification-management/
|
|
├── app.config.js # Complete application config
|
|
├── pages/
|
|
│ ├── dashboard/
|
|
│ ├── send/
|
|
│ ├── templates/
|
|
│ ├── history/
|
|
│ ├── settings/
|
|
│ └── reports/
|
|
├── components/
|
|
│ ├── NotificationSender.vue
|
|
│ ├── NotificationList.vue
|
|
│ └── NotificationStats.vue
|
|
├── server/
|
|
│ └── api/
|
|
│ ├── notifications/
|
|
│ └── templates/
|
|
├── composables/
|
|
│ └── useNotifications.js
|
|
├── migrations/
|
|
│ ├── 001_create_notifications.sql
|
|
│ ├── 002_create_templates.sql
|
|
│ └── 003_create_schedules.sql
|
|
└── scripts/
|
|
├── pre-install.js
|
|
├── post-install.js
|
|
└── pre-uninstall.js</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Recent Uploads -->
|
|
<rs-card>
|
|
<template #header>
|
|
<div class="flex items-center">
|
|
<Icon name="mdi:history" class="mr-2" />
|
|
Recent Uploads
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="space-y-3">
|
|
<div class="flex items-center justify-between py-2 border-b">
|
|
<div class="flex items-center space-x-3">
|
|
<Icon name="mdi:check-circle" class="text-green-500" size="16" />
|
|
<div>
|
|
<p class="text-sm font-medium">Report Management System</p>
|
|
<p class="text-xs text-gray-500">Uploaded 3 days ago</p>
|
|
</div>
|
|
</div>
|
|
<rs-badge variant="success" size="sm">Success</rs-badge>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between py-2 border-b">
|
|
<div class="flex items-center space-x-3">
|
|
<Icon name="mdi:check-circle" class="text-green-500" size="16" />
|
|
<div>
|
|
<p class="text-sm font-medium">Notification Management System</p>
|
|
<p class="text-xs text-gray-500">Uploaded 1 week ago</p>
|
|
</div>
|
|
</div>
|
|
<rs-badge variant="success" size="sm">Success</rs-badge>
|
|
</div>
|
|
|
|
<div class="text-center py-4 text-gray-500 text-sm">
|
|
Ready to upload your next CORRAD+ application
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template> |