@akinon/ui-shell-dev 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/index-CuIRnKVE.css +1 -0
- package/dist/assets/index-DUUd718F.js +193 -0
- package/dist/index.html +33 -0
- package/index.html +32 -0
- package/index.mjs +1 -0
- package/package.json +49 -0
- package/src/components/AppLayout.tsx +64 -0
- package/src/components/Header.tsx +66 -0
- package/src/components/PluginLoader.tsx +112 -0
- package/src/components/Sidebar.tsx +74 -0
- package/src/index.tsx +4 -0
- package/src/main.tsx +29 -0
- package/src/server/dev-server.mjs +193 -0
- package/src/server/dev-server.ts +142 -0
- package/src/shell-app.tsx +17 -0
- package/src/themes/index.ts +37 -0
- package/src/themes/omnitron.ts +66 -0
- package/src/types.ts +40 -0
- package/tsconfig.json +11 -0
- package/vite.config.ts +25 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import cors from 'cors';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { createServer as createViteServer } from 'vite';
|
|
6
|
+
|
|
7
|
+
import type { ShellConfig } from '../types';
|
|
8
|
+
|
|
9
|
+
export class ShellDevServer {
|
|
10
|
+
private app: express.Application;
|
|
11
|
+
private config: ShellConfig;
|
|
12
|
+
private viteServer?: import('vite').ViteDevServer;
|
|
13
|
+
|
|
14
|
+
constructor(config: ShellConfig) {
|
|
15
|
+
this.app = express();
|
|
16
|
+
this.config = config;
|
|
17
|
+
this.setupMiddleware();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private setupMiddleware() {
|
|
21
|
+
// Enable CORS for plugin communication
|
|
22
|
+
this.app.use(
|
|
23
|
+
cors({
|
|
24
|
+
origin: true,
|
|
25
|
+
credentials: true
|
|
26
|
+
})
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// Parse JSON bodies
|
|
30
|
+
this.app.use(express.json());
|
|
31
|
+
|
|
32
|
+
// Serve static assets
|
|
33
|
+
this.app.use('/assets', express.static(path.join(__dirname, '../assets')));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async start() {
|
|
37
|
+
try {
|
|
38
|
+
// Create Vite server for the shell app
|
|
39
|
+
this.viteServer = await createViteServer({
|
|
40
|
+
server: {
|
|
41
|
+
middlewareMode: true,
|
|
42
|
+
cors: true
|
|
43
|
+
},
|
|
44
|
+
appType: 'spa',
|
|
45
|
+
root: path.resolve(__dirname, '..')
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Use Vite middleware
|
|
49
|
+
this.app.use(this.viteServer.ssrFixStacktrace);
|
|
50
|
+
this.app.use(this.viteServer.middlewares);
|
|
51
|
+
|
|
52
|
+
// API endpoints
|
|
53
|
+
this.setupApiRoutes();
|
|
54
|
+
|
|
55
|
+
// Serve the shell app
|
|
56
|
+
this.app.get('*', async (req, res, next) => {
|
|
57
|
+
if (req.path.startsWith('/api')) {
|
|
58
|
+
return next();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
let template = fs.readFileSync(
|
|
63
|
+
path.resolve(__dirname, '../index.html'),
|
|
64
|
+
'utf-8'
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
template = await this.viteServer.transformIndexHtml(
|
|
68
|
+
req.url,
|
|
69
|
+
template
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Inject config
|
|
73
|
+
template = template.replace(
|
|
74
|
+
'<!--SHELL_CONFIG-->',
|
|
75
|
+
`<script>window.__SHELL_CONFIG__ = ${JSON.stringify(this.config)};</script>`
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
res.status(200).set({ 'Content-Type': 'text/html' }).end(template);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
this.viteServer.ssrFixStacktrace(error);
|
|
81
|
+
next(error);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Start server
|
|
86
|
+
const server = this.app.listen(this.config.shell.port, () => {
|
|
87
|
+
console.log(
|
|
88
|
+
`🚀 Shell development server running on http://localhost:${this.config.shell.port}`
|
|
89
|
+
);
|
|
90
|
+
console.log(`📱 Plugin expected at: ${this.config.plugin.url}`);
|
|
91
|
+
console.log(`🎨 Theme: ${this.config.shell.theme}`);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Handle graceful shutdown
|
|
95
|
+
process.on('SIGTERM', () => {
|
|
96
|
+
console.log('🛑 Shutting down shell server...');
|
|
97
|
+
server.close(() => {
|
|
98
|
+
if (this.viteServer) {
|
|
99
|
+
this.viteServer.close();
|
|
100
|
+
}
|
|
101
|
+
process.exit(0);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return server;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error('❌ Failed to start shell server:', error);
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private setupApiRoutes() {
|
|
113
|
+
// Health check
|
|
114
|
+
this.app.get('/api/health', (req, res) => {
|
|
115
|
+
res.json({
|
|
116
|
+
status: 'ok',
|
|
117
|
+
config: this.config,
|
|
118
|
+
timestamp: new Date().toISOString()
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Configuration endpoint
|
|
123
|
+
this.app.get('/api/config', (req, res) => {
|
|
124
|
+
res.json(this.config);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Plugin proxy endpoint (for development)
|
|
128
|
+
this.app.get('/api/plugin/health', async (req, res) => {
|
|
129
|
+
try {
|
|
130
|
+
const response = await fetch(`${this.config.plugin.url}/api/health`);
|
|
131
|
+
const data = await response.json();
|
|
132
|
+
res.json({ plugin: data, shell: { status: 'ok' } });
|
|
133
|
+
} catch (error) {
|
|
134
|
+
res.status(502).json({
|
|
135
|
+
error: 'Plugin not available',
|
|
136
|
+
plugin_url: this.config.plugin.url,
|
|
137
|
+
message: error.message
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { AkinonUiProvider } from '@akinon/ui-system';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import { AppLayout } from './components/AppLayout';
|
|
5
|
+
import type { ShellConfig } from './types';
|
|
6
|
+
|
|
7
|
+
interface ShellAppProps {
|
|
8
|
+
config: ShellConfig;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const ShellApp: React.FC<ShellAppProps> = ({ config }) => {
|
|
12
|
+
return (
|
|
13
|
+
<AkinonUiProvider>
|
|
14
|
+
<AppLayout config={config} />
|
|
15
|
+
</AkinonUiProvider>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { SidebarItem, ThemeConfig } from '../types';
|
|
2
|
+
import { omnitronSidebarItems, omnitronTheme } from './omnitron';
|
|
3
|
+
|
|
4
|
+
export const themes: Record<string, ThemeConfig> = {
|
|
5
|
+
omnitron: omnitronTheme,
|
|
6
|
+
'seller-center': omnitronTheme, // Use same for now
|
|
7
|
+
minimal: {
|
|
8
|
+
...omnitronTheme,
|
|
9
|
+
name: 'minimal',
|
|
10
|
+
colors: {
|
|
11
|
+
...omnitronTheme.colors,
|
|
12
|
+
sidebar: '#ffffff',
|
|
13
|
+
background: '#ffffff'
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const defaultSidebarItems: Record<string, SidebarItem[]> = {
|
|
19
|
+
omnitron: omnitronSidebarItems,
|
|
20
|
+
'seller-center': omnitronSidebarItems,
|
|
21
|
+
minimal: [
|
|
22
|
+
{
|
|
23
|
+
key: 'plugin',
|
|
24
|
+
label: 'Plugin Area',
|
|
25
|
+
icon: 'plugin',
|
|
26
|
+
active: true
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function getTheme(themeName: string): ThemeConfig {
|
|
32
|
+
return themes[themeName] || themes.omnitron;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getSidebarItems(themeName: string): SidebarItem[] {
|
|
36
|
+
return defaultSidebarItems[themeName] || defaultSidebarItems.omnitron;
|
|
37
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ThemeConfig } from '../types';
|
|
2
|
+
|
|
3
|
+
export const omnitronTheme: ThemeConfig = {
|
|
4
|
+
name: 'omnitron',
|
|
5
|
+
colors: {
|
|
6
|
+
primary: '#1890ff',
|
|
7
|
+
secondary: '#52c41a',
|
|
8
|
+
background: '#f0f2f5',
|
|
9
|
+
sidebar: '#001529',
|
|
10
|
+
header: '#ffffff'
|
|
11
|
+
},
|
|
12
|
+
sidebar: {
|
|
13
|
+
width: 240,
|
|
14
|
+
collapsedWidth: 80
|
|
15
|
+
},
|
|
16
|
+
header: {
|
|
17
|
+
height: 64
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const omnitronSidebarItems = [
|
|
22
|
+
{
|
|
23
|
+
key: '1',
|
|
24
|
+
label: 'Dashboard',
|
|
25
|
+
icon: 'dashboard'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
key: '2',
|
|
29
|
+
label: 'Products',
|
|
30
|
+
icon: 'shopping',
|
|
31
|
+
children: [
|
|
32
|
+
{ key: '2-1', label: 'Product List' },
|
|
33
|
+
{ key: '2-2', label: 'Categories' },
|
|
34
|
+
{ key: '2-3', label: 'Attributes' }
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
key: '3',
|
|
39
|
+
label: 'Orders',
|
|
40
|
+
icon: 'shopping-cart',
|
|
41
|
+
children: [
|
|
42
|
+
{ key: '3-1', label: 'Order List' },
|
|
43
|
+
{ key: '3-2', label: 'Order Status' }
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
key: '4',
|
|
48
|
+
label: 'Customers',
|
|
49
|
+
icon: 'user'
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
key: '5',
|
|
53
|
+
label: 'Reports',
|
|
54
|
+
icon: 'chart',
|
|
55
|
+
children: [
|
|
56
|
+
{ key: '5-1', label: 'Sales Reports' },
|
|
57
|
+
{ key: '5-2', label: 'Customer Reports' }
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
key: 'plugin',
|
|
62
|
+
label: 'Plugin Area',
|
|
63
|
+
icon: 'plugin',
|
|
64
|
+
active: true
|
|
65
|
+
}
|
|
66
|
+
];
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface ShellConfig {
|
|
2
|
+
plugin: {
|
|
3
|
+
url: string;
|
|
4
|
+
path: string;
|
|
5
|
+
};
|
|
6
|
+
shell: {
|
|
7
|
+
port: number;
|
|
8
|
+
theme: 'omnitron' | 'seller-center' | 'minimal';
|
|
9
|
+
title: string;
|
|
10
|
+
};
|
|
11
|
+
sidebar: {
|
|
12
|
+
items: SidebarItem[];
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SidebarItem {
|
|
17
|
+
key: string;
|
|
18
|
+
label: string;
|
|
19
|
+
icon?: string;
|
|
20
|
+
active?: boolean;
|
|
21
|
+
children?: SidebarItem[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ThemeConfig {
|
|
25
|
+
name: string;
|
|
26
|
+
colors: {
|
|
27
|
+
primary: string;
|
|
28
|
+
secondary: string;
|
|
29
|
+
background: string;
|
|
30
|
+
sidebar: string;
|
|
31
|
+
header: string;
|
|
32
|
+
};
|
|
33
|
+
sidebar: {
|
|
34
|
+
width: number;
|
|
35
|
+
collapsedWidth: number;
|
|
36
|
+
};
|
|
37
|
+
header: {
|
|
38
|
+
height: number;
|
|
39
|
+
};
|
|
40
|
+
}
|
package/tsconfig.json
ADDED
package/vite.config.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import react from '@vitejs/plugin-react-swc';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { defineConfig } from 'vite';
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [react()],
|
|
7
|
+
resolve: {
|
|
8
|
+
alias: {
|
|
9
|
+
'@': path.resolve(__dirname, './src')
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
server: {
|
|
13
|
+
port: 4000,
|
|
14
|
+
cors: true
|
|
15
|
+
},
|
|
16
|
+
build: {
|
|
17
|
+
outDir: 'dist',
|
|
18
|
+
rollupOptions: {
|
|
19
|
+
// Exclude server files from build
|
|
20
|
+
external: (id) => {
|
|
21
|
+
return id.includes('/server/') || id.includes('server');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
});
|