init
This commit is contained in:
3
router-app/static/packages/config.js
Normal file
3
router-app/static/packages/config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export const config = {
|
||||
appListUrl: 'http://localhost:4005/api/router?path=app&key=public-list',
|
||||
};
|
||||
45
router-app/static/packages/electron.js
Normal file
45
router-app/static/packages/electron.js
Normal file
@@ -0,0 +1,45 @@
|
||||
export const checkIsElectron = () => {
|
||||
return typeof window !== 'undefined' && typeof window.electron === 'object';
|
||||
};
|
||||
export const getElectron = () => {
|
||||
return window.electron;
|
||||
};
|
||||
export const getAppList = async () => {
|
||||
const check = checkIsElectron();
|
||||
if (!check) {
|
||||
console.log('not electron');
|
||||
return [];
|
||||
}
|
||||
const electron = getElectron();
|
||||
console.log('electron', electron);
|
||||
const appList = await electron.ipcRenderer.invoke('get-app-list');
|
||||
|
||||
console.log('appList', appList);
|
||||
return appList;
|
||||
};
|
||||
|
||||
export const installApp = async (app) => {
|
||||
const check = checkIsElectron();
|
||||
if (!check) {
|
||||
console.log('not electron');
|
||||
return [];
|
||||
}
|
||||
const electron = getElectron();
|
||||
console.log('installApp', app);
|
||||
const result = await electron.ipcRenderer.invoke('install-app', app);
|
||||
console.log('installApp result', result);
|
||||
return result;
|
||||
};
|
||||
|
||||
export const uninstallApp = async (app) => {
|
||||
const check = checkIsElectron();
|
||||
if (!check) {
|
||||
console.log('not electron');
|
||||
return [];
|
||||
}
|
||||
const electron = getElectron();
|
||||
console.log('uninstallApp', app);
|
||||
const result = await electron.ipcRenderer.invoke('uninstall-app', app);
|
||||
console.log('uninstallApp result', result);
|
||||
return result;
|
||||
};
|
||||
17
router-app/static/packages/index.html
Normal file
17
router-app/static/packages/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Package Manager</title>
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
<script type="module" src="./electron.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<h1>Package Manager</h1>
|
||||
<div id="package-list" class="package-list"></div>
|
||||
</div>
|
||||
<script type="module" src="./main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
158
router-app/static/packages/main.js
Normal file
158
router-app/static/packages/main.js
Normal file
@@ -0,0 +1,158 @@
|
||||
import { config } from './config.js';
|
||||
import { getAppList, installApp, uninstallApp } from './electron.js';
|
||||
let installedPackages = [];
|
||||
let allPackages = [];
|
||||
// Store for installed packages
|
||||
// const installedPackages = [
|
||||
// { user: 'test', key: 'test-key', version: '1.0.0' },
|
||||
// { user: 'demo', key: 'demo-package', version: '1.2.0' },
|
||||
// ];
|
||||
|
||||
// Function to fetch packages from API
|
||||
async function fetchPackages() {
|
||||
try {
|
||||
// Currently using mock data
|
||||
// TODO: Uncomment the following code when API is ready
|
||||
const response = await fetch(config.appListUrl);
|
||||
const result = await response.json();
|
||||
if (result.code === 200) {
|
||||
return result.data;
|
||||
}
|
||||
throw new Error('Failed to fetch packages');
|
||||
} catch (error) {
|
||||
console.error('Error fetching packages:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Mock data for testing
|
||||
const mockPackages = [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Demo Package 1',
|
||||
description: 'A test package for demonstration',
|
||||
version: '1.0.0',
|
||||
user: 'test',
|
||||
key: 'test-key',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Demo Package 2',
|
||||
description: 'Another test package with updates',
|
||||
version: '2.0.0',
|
||||
user: 'demo',
|
||||
key: 'demo-package',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'New Package',
|
||||
description: "A package that hasn't been installed yet",
|
||||
version: '1.0.0',
|
||||
user: 'demo',
|
||||
key: 'new-package',
|
||||
},
|
||||
];
|
||||
|
||||
// Function to check if a package is installed
|
||||
async function getPackageStatus(pkg) {
|
||||
const installed = installedPackages.find((p) => p.user === pkg.user && p.key === pkg.key);
|
||||
|
||||
if (!installed) return 'not-installed';
|
||||
if (installed.version !== pkg.version) return 'update-available';
|
||||
return 'installed';
|
||||
}
|
||||
|
||||
// Function to create a package card
|
||||
async function createPackageCard(pkg) {
|
||||
const status = await getPackageStatus(pkg);
|
||||
const card = document.createElement('div');
|
||||
card.className = 'package-card';
|
||||
|
||||
card.innerHTML = `
|
||||
<h2>${pkg.title}</h2>
|
||||
<p class="description">${pkg.description}</p>
|
||||
<div class="package-info">
|
||||
<span>Version: ${pkg.version}</span>
|
||||
<span>User: ${pkg.user}</span>
|
||||
</div>
|
||||
<div class="actions">
|
||||
${getActionButton(status, pkg)}
|
||||
${status !== 'not-installed' ? `<button class="button button-uninstall" onclick="handleUninstall('${pkg.id}')">Uninstall</button>` : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
// Function to get the appropriate action button based on status
|
||||
function getActionButton(status, pkg) {
|
||||
switch (status) {
|
||||
case 'not-installed':
|
||||
return `<button class="button button-install" onclick="handleInstall('${pkg.id}')">Install</button>`;
|
||||
case 'update-available':
|
||||
return `<button class="button button-update" onclick="handleUpdate('${pkg.id}')">Update</button>`;
|
||||
case 'installed':
|
||||
return `<button class="button button-reinstall" onclick="handleReinstall('${pkg.id}')">Reinstall</button>`;
|
||||
}
|
||||
}
|
||||
|
||||
// Action handlers
|
||||
window.handleInstall = async (id) => {
|
||||
console.log('Installing package:', id);
|
||||
const pkg = allPackages.find((p) => p.id === id);
|
||||
if (pkg) {
|
||||
await installApp(pkg);
|
||||
renderPackages();
|
||||
}
|
||||
};
|
||||
|
||||
window.handleUpdate = async (id) => {
|
||||
console.log('Updating package:', id);
|
||||
const pkg = allPackages.find((p) => p.id === id);
|
||||
if (pkg) {
|
||||
await installApp(pkg);
|
||||
renderPackages();
|
||||
}
|
||||
};
|
||||
|
||||
window.handleReinstall = async (id) => {
|
||||
console.log('Reinstalling package:', id);
|
||||
const pkg = allPackages.find((p) => p.id === id);
|
||||
if (pkg) {
|
||||
await installApp(pkg);
|
||||
renderPackages();
|
||||
}
|
||||
};
|
||||
|
||||
window.handleUninstall = async (id) => {
|
||||
console.log('Uninstalling package:', id);
|
||||
// const pkg = mockPackages.find((p) => p.id === id);
|
||||
const pkg = allPackages.find((p) => p.id === id);
|
||||
if (pkg) {
|
||||
// TODO: Replace with actual API call
|
||||
const index = installedPackages.findIndex((p) => p.user === pkg.user && p.key === pkg.key);
|
||||
await uninstallApp(pkg);
|
||||
if (index !== -1) {
|
||||
installedPackages.splice(index, 1);
|
||||
renderPackages();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Render packages
|
||||
async function renderPackages() {
|
||||
const packageList = document.getElementById('package-list');
|
||||
packageList.innerHTML = '';
|
||||
const installed = await getAppList();
|
||||
installedPackages = installed;
|
||||
for (const pkg of allPackages) {
|
||||
packageList.appendChild(await createPackageCard(pkg));
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the application
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const packages = await fetchPackages();
|
||||
allPackages = packages;
|
||||
renderPackages();
|
||||
});
|
||||
115
router-app/static/packages/style.css
Normal file
115
router-app/static/packages/style.css
Normal file
@@ -0,0 +1,115 @@
|
||||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
color-scheme: light dark;
|
||||
background-color: #fff8e1;
|
||||
color: #213547;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #ff8f00;
|
||||
}
|
||||
|
||||
.package-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.package-card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 2px 4px rgba(255, 143, 0, 0.1);
|
||||
border: 1px solid #ffe0b2;
|
||||
}
|
||||
|
||||
.package-card h2 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: #f57c00;
|
||||
}
|
||||
|
||||
.package-card .description {
|
||||
color: #666;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.9rem;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 4;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1.5;
|
||||
max-height: 6em;
|
||||
}
|
||||
|
||||
.package-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.button-install {
|
||||
background-color: #ffa000;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.button-update {
|
||||
background-color: #ff8f00;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.button-reinstall {
|
||||
background-color: #ffb300;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.button-uninstall {
|
||||
background-color: #ff6f00;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.button:disabled {
|
||||
background-color: #ffe0b2;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
text-align: center;
|
||||
color: #ff6f00;
|
||||
padding: 2rem;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(255, 143, 0, 0.1);
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
Reference in New Issue
Block a user