-
This commit is contained in:
2
demo/dist/remoteEntry.js
vendored
2
demo/dist/remoteEntry.js
vendored
File diff suppressed because one or more lines are too long
@ -1,6 +1,9 @@
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const { ModuleFederationPlugin } = require('webpack').container;
|
||||
|
||||
// Import the microfrontends registry
|
||||
const { getExposesConfig } = require('../web/src/microfrontends.js');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
entry: './src/index.tsx',
|
||||
@ -38,9 +41,7 @@ module.exports = {
|
||||
new ModuleFederationPlugin({
|
||||
name: 'demo',
|
||||
filename: 'remoteEntry.js',
|
||||
exposes: {
|
||||
'./App': './src/App.tsx',
|
||||
},
|
||||
exposes: getExposesConfig('demo'),
|
||||
shared: {
|
||||
react: {
|
||||
singleton: true,
|
||||
@ -73,4 +74,4 @@ module.exports = {
|
||||
template: './public/index.html',
|
||||
}),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
1
faas/web/.gitignore
vendored
Normal file
1
faas/web/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
dist
|
||||
@ -2,6 +2,9 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const { ModuleFederationPlugin } = require('webpack').container;
|
||||
const webpack = require('webpack');
|
||||
|
||||
// Import the microfrontends registry
|
||||
const { getExposesConfig } = require('../../web/src/microfrontends.js');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
entry: './src/index.tsx',
|
||||
@ -39,9 +42,7 @@ module.exports = {
|
||||
new ModuleFederationPlugin({
|
||||
name: 'faas',
|
||||
filename: 'remoteEntry.js',
|
||||
exposes: {
|
||||
'./App': './src/App.tsx',
|
||||
},
|
||||
exposes: getExposesConfig('faas'),
|
||||
shared: {
|
||||
react: {
|
||||
singleton: true,
|
||||
@ -82,4 +83,4 @@ module.exports = {
|
||||
'process.env': JSON.stringify(process.env),
|
||||
}),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
2
kms/web/dist/665.js
vendored
2
kms/web/dist/665.js
vendored
File diff suppressed because one or more lines are too long
2
kms/web/dist/main.js
vendored
2
kms/web/dist/main.js
vendored
File diff suppressed because one or more lines are too long
2
kms/web/dist/remoteEntry.js
vendored
2
kms/web/dist/remoteEntry.js
vendored
File diff suppressed because one or more lines are too long
@ -1,7 +1,11 @@
|
||||
const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const { ModuleFederationPlugin } = require('webpack').container;
|
||||
const webpack = require('webpack');
|
||||
|
||||
// Import the microfrontends registry
|
||||
const { getExposesConfig } = require('../../web/src/microfrontends.js');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
entry: './src/index.tsx',
|
||||
@ -39,9 +43,7 @@ module.exports = {
|
||||
new ModuleFederationPlugin({
|
||||
name: 'kms',
|
||||
filename: 'remoteEntry.js',
|
||||
exposes: {
|
||||
'./App': './src/App.tsx',
|
||||
},
|
||||
exposes: getExposesConfig('kms'),
|
||||
shared: {
|
||||
react: {
|
||||
singleton: true,
|
||||
@ -82,4 +84,4 @@ module.exports = {
|
||||
'process.env': JSON.stringify(process.env),
|
||||
}),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
2
web/dist/main.js
vendored
2
web/dist/main.js
vendored
File diff suppressed because one or more lines are too long
@ -3,10 +3,22 @@ import { useParams } from 'react-router-dom';
|
||||
import { Loader, Center, Text, Stack, Alert } from '@mantine/core';
|
||||
import { IconAlertCircle } from '@tabler/icons-react';
|
||||
import Breadcrumbs from './Breadcrumbs';
|
||||
import { microFrontends } from '../microfrontends.js';
|
||||
|
||||
const DemoApp = React.lazy(() => import('demo/App'));
|
||||
const KMSApp = React.lazy(() => import('kms/App'));
|
||||
const FaaSApp = React.lazy(() => import('faas/App'));
|
||||
// Pre-defined lazy imports for each microfrontend
|
||||
// @ts-ignore - These modules are loaded at runtime via Module Federation
|
||||
const DemoApp = React.lazy(() => import('demo/src/App'));
|
||||
// @ts-ignore - These modules are loaded at runtime via Module Federation
|
||||
const KMSApp = React.lazy(() => import('kms/src/App'));
|
||||
// @ts-ignore - These modules are loaded at runtime via Module Federation
|
||||
const FaaSApp = React.lazy(() => import('faas/src/App'));
|
||||
|
||||
// Map app names to components
|
||||
const appComponents: Record<string, React.LazyExoticComponent<React.ComponentType<any>>> = {
|
||||
demo: DemoApp,
|
||||
kms: KMSApp,
|
||||
faas: FaaSApp,
|
||||
};
|
||||
|
||||
const AppLoader: React.FC = () => {
|
||||
const { appName } = useParams<{ appName: string }>();
|
||||
@ -32,32 +44,21 @@ const AppLoader: React.FC = () => {
|
||||
);
|
||||
|
||||
const renderApp = () => {
|
||||
switch (appName) {
|
||||
case 'demo':
|
||||
return (
|
||||
<Suspense fallback={<LoadingFallback />}>
|
||||
<DemoApp />
|
||||
</Suspense>
|
||||
);
|
||||
case 'kms':
|
||||
return (
|
||||
<Suspense fallback={<LoadingFallback />}>
|
||||
<KMSApp />
|
||||
</Suspense>
|
||||
);
|
||||
case 'faas':
|
||||
return (
|
||||
<Suspense fallback={<LoadingFallback />}>
|
||||
<FaaSApp />
|
||||
</Suspense>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<ErrorFallback
|
||||
error={`Application "${appName}" is not available or not configured.`}
|
||||
/>
|
||||
);
|
||||
// Check if the app is registered in our microfrontends registry
|
||||
if (appName && appComponents[appName] && microFrontends[appName]) {
|
||||
const App = appComponents[appName];
|
||||
return (
|
||||
<Suspense fallback={<LoadingFallback />}>
|
||||
<App />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ErrorFallback
|
||||
error={`Application "${appName}" is not available or not configured.`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -68,4 +69,4 @@ const AppLoader: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default AppLoader;
|
||||
export default AppLoader;
|
||||
|
||||
@ -1,44 +1,89 @@
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { NavLink, Stack, Text } from '@mantine/core';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import {
|
||||
IconDashboard,
|
||||
IconKey,
|
||||
IconFunction,
|
||||
IconApps,
|
||||
} from '@tabler/icons-react';
|
||||
import { microFrontends } from '../microfrontends.js';
|
||||
|
||||
// Map microfrontend IDs to display information
|
||||
const getAppInfo = (id: string) => {
|
||||
const appInfo: Record<string, { label: string; icon: React.ComponentType<any>; category: string }> = {
|
||||
demo: {
|
||||
label: 'Demo App',
|
||||
icon: IconDashboard,
|
||||
category: 'Development',
|
||||
},
|
||||
kms: {
|
||||
label: 'Key Management',
|
||||
icon: IconKey,
|
||||
category: 'Security',
|
||||
},
|
||||
faas: {
|
||||
label: 'Functions',
|
||||
icon: IconFunction,
|
||||
category: 'Development',
|
||||
},
|
||||
};
|
||||
|
||||
return appInfo[id] || {
|
||||
label: id.charAt(0).toUpperCase() + id.slice(1),
|
||||
icon: IconApps,
|
||||
category: 'Application',
|
||||
};
|
||||
};
|
||||
|
||||
const Navigation: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
// Define all available applications
|
||||
const allAvailableApplications = [
|
||||
{
|
||||
label: 'Demo App',
|
||||
icon: IconDashboard,
|
||||
path: '/app/demo',
|
||||
},
|
||||
{
|
||||
label: 'Key Management',
|
||||
icon: IconKey,
|
||||
path: '/app/kms',
|
||||
},
|
||||
{
|
||||
label: 'Functions',
|
||||
icon: IconFunction,
|
||||
path: '/app/faas',
|
||||
},
|
||||
];
|
||||
// Load favorites from localStorage (same as HomePage)
|
||||
const [favoriteApps, setFavoriteApps] = useState<string[]>(() => {
|
||||
try {
|
||||
const saved = localStorage.getItem('favoriteMicrofrontends');
|
||||
return saved ? JSON.parse(saved) : ['demo'];
|
||||
} catch {
|
||||
return ['demo'];
|
||||
}
|
||||
});
|
||||
|
||||
// Define which apps are favorited (you could make this dynamic later)
|
||||
const favoritePaths = ['/app/demo'];
|
||||
// Listen for changes to favorites in localStorage (in case they change in HomePage)
|
||||
useEffect(() => {
|
||||
const handleStorageChange = () => {
|
||||
try {
|
||||
const saved = localStorage.getItem('favoriteMicrofrontends');
|
||||
if (saved) {
|
||||
setFavoriteApps(JSON.parse(saved));
|
||||
}
|
||||
} catch {
|
||||
// If parsing fails, keep current state
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('storage', handleStorageChange);
|
||||
return () => window.removeEventListener('storage', handleStorageChange);
|
||||
}, []);
|
||||
|
||||
// Generate all available applications from the microfrontends registry
|
||||
const allAvailableApplications = Object.keys(microFrontends).map(id => {
|
||||
const info = getAppInfo(id);
|
||||
return {
|
||||
id,
|
||||
label: info.label,
|
||||
icon: info.icon,
|
||||
path: `/app/${id}`,
|
||||
};
|
||||
});
|
||||
|
||||
const favoriteApplications = allAvailableApplications.filter(app =>
|
||||
favoritePaths.includes(app.path)
|
||||
favoriteApps.includes(app.id)
|
||||
);
|
||||
|
||||
const nonFavoriteApplications = allAvailableApplications.filter(app =>
|
||||
!favoritePaths.includes(app.path)
|
||||
!favoriteApps.includes(app.id)
|
||||
);
|
||||
|
||||
return (
|
||||
@ -78,4 +123,4 @@ const Navigation: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Navigation;
|
||||
export default Navigation;
|
||||
|
||||
59
web/src/microfrontends.js
Normal file
59
web/src/microfrontends.js
Normal file
@ -0,0 +1,59 @@
|
||||
// Centralized registry for all microfrontends
|
||||
// This file contains the single source of truth for all microfrontend configurations
|
||||
|
||||
// Define all microfrontends
|
||||
export const microFrontends = {
|
||||
demo: {
|
||||
name: 'demo',
|
||||
url: 'http://localhost:3001',
|
||||
port: 3001,
|
||||
exposedModule: './src/App',
|
||||
importPath: 'demo/src/App',
|
||||
},
|
||||
kms: {
|
||||
name: 'kms',
|
||||
url: 'http://localhost:3002',
|
||||
port: 3002,
|
||||
exposedModule: './src/App',
|
||||
importPath: 'kms/src/App',
|
||||
},
|
||||
faas: {
|
||||
name: 'faas',
|
||||
url: 'http://localhost:3003',
|
||||
port: 3003,
|
||||
exposedModule: './src/App',
|
||||
importPath: 'faas/src/App',
|
||||
},
|
||||
};
|
||||
|
||||
// Generate remotes configuration for Module Federation
|
||||
export const getRemotesConfig = () => {
|
||||
const remotes = {};
|
||||
|
||||
Object.values(microFrontends).forEach(mf => {
|
||||
remotes[mf.name] = `${mf.name}@${mf.url}/remoteEntry.js`;
|
||||
});
|
||||
|
||||
return remotes;
|
||||
};
|
||||
|
||||
// Generate exposes configuration for Module Federation (for microfrontends)
|
||||
export const getExposesConfig = (appName) => {
|
||||
const appConfig = microFrontends[appName];
|
||||
if (!appConfig) {
|
||||
throw new Error(`Microfrontend ${appName} not found in registry`);
|
||||
}
|
||||
|
||||
const exposes = {};
|
||||
exposes[appConfig.exposedModule] = appConfig.exposedModule;
|
||||
|
||||
return exposes;
|
||||
};
|
||||
|
||||
// Get app loader configuration for AppLoader component
|
||||
export const getAppLoaderConfig = () => {
|
||||
return Object.keys(microFrontends).map(name => ({
|
||||
name,
|
||||
importPath: microFrontends[name].importPath,
|
||||
}));
|
||||
};
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
SimpleGrid,
|
||||
Card,
|
||||
@ -16,8 +16,10 @@ import {
|
||||
IconDashboard,
|
||||
IconKey,
|
||||
IconFunction,
|
||||
IconApps,
|
||||
} from '@tabler/icons-react';
|
||||
import Breadcrumbs from '../components/Breadcrumbs';
|
||||
import { microFrontends } from '../microfrontends.js';
|
||||
|
||||
interface App {
|
||||
id: string;
|
||||
@ -28,36 +30,71 @@ interface App {
|
||||
category: string;
|
||||
}
|
||||
|
||||
const HomePage: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const [favoriteApps, setFavoriteApps] = useState<string[]>(['demo']);
|
||||
|
||||
const availableApps: App[] = [
|
||||
{
|
||||
id: 'demo',
|
||||
// Map microfrontend IDs to display information
|
||||
const getAppInfo = (id: string) => {
|
||||
const appInfo: Record<string, { name: string; description: string; icon: React.ComponentType<any>; category: string }> = {
|
||||
demo: {
|
||||
name: 'Demo App',
|
||||
description: 'Sample microfrontend application for testing module federation',
|
||||
icon: IconDashboard,
|
||||
path: '/app/demo',
|
||||
category: 'Development',
|
||||
},
|
||||
{
|
||||
id: 'kms',
|
||||
kms: {
|
||||
name: 'Key Management System',
|
||||
description: 'Manage API keys, tokens, and access permissions for applications',
|
||||
icon: IconKey,
|
||||
path: '/app/kms',
|
||||
category: 'Security',
|
||||
},
|
||||
{
|
||||
id: 'faas',
|
||||
faas: {
|
||||
name: 'Function-as-a-Service',
|
||||
description: 'Deploy and manage serverless functions',
|
||||
icon: IconFunction,
|
||||
path: '/app/faas',
|
||||
category: 'Development',
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
return appInfo[id] || {
|
||||
name: id.charAt(0).toUpperCase() + id.slice(1),
|
||||
description: `Microfrontend application: ${id}`,
|
||||
icon: IconApps,
|
||||
category: 'Application',
|
||||
};
|
||||
};
|
||||
|
||||
const HomePage: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Load favorites from localStorage or default to demo app
|
||||
const [favoriteApps, setFavoriteApps] = useState<string[]>(() => {
|
||||
try {
|
||||
const saved = localStorage.getItem('favoriteMicrofrontends');
|
||||
return saved ? JSON.parse(saved) : ['demo'];
|
||||
} catch {
|
||||
return ['demo'];
|
||||
}
|
||||
});
|
||||
|
||||
// Save favorites to localStorage whenever they change
|
||||
useEffect(() => {
|
||||
try {
|
||||
localStorage.setItem('favoriteMicrofrontends', JSON.stringify(favoriteApps));
|
||||
} catch (e) {
|
||||
console.warn('Failed to save favorites to localStorage', e);
|
||||
}
|
||||
}, [favoriteApps]);
|
||||
|
||||
// Generate available apps from the microfrontends registry
|
||||
const availableApps: App[] = Object.keys(microFrontends).map(id => {
|
||||
const info = getAppInfo(id);
|
||||
return {
|
||||
id,
|
||||
name: info.name,
|
||||
description: info.description,
|
||||
icon: info.icon,
|
||||
path: `/app/${id}`,
|
||||
category: info.category,
|
||||
};
|
||||
});
|
||||
|
||||
const toggleFavorite = (appId: string) => {
|
||||
setFavoriteApps(prev =>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const { ModuleFederationPlugin } = require('webpack').container;
|
||||
const { getRemotesConfig } = require('./src/microfrontends.js');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
@ -44,11 +45,7 @@ module.exports = {
|
||||
plugins: [
|
||||
new ModuleFederationPlugin({
|
||||
name: 'shell',
|
||||
remotes: {
|
||||
demo: 'demo@http://localhost:3001/remoteEntry.js',
|
||||
kms: 'kms@http://localhost:3002/remoteEntry.js',
|
||||
faas: 'faas@http://localhost:3003/remoteEntry.js',
|
||||
},
|
||||
remotes: getRemotesConfig(),
|
||||
shared: {
|
||||
react: {
|
||||
singleton: true,
|
||||
@ -86,4 +83,4 @@ module.exports = {
|
||||
template: './public/index.html',
|
||||
}),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user