-
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 HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
const { ModuleFederationPlugin } = require('webpack').container;
|
const { ModuleFederationPlugin } = require('webpack').container;
|
||||||
|
|
||||||
|
// Import the microfrontends registry
|
||||||
|
const { getExposesConfig } = require('../web/src/microfrontends.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: 'development',
|
mode: 'development',
|
||||||
entry: './src/index.tsx',
|
entry: './src/index.tsx',
|
||||||
@ -38,9 +41,7 @@ module.exports = {
|
|||||||
new ModuleFederationPlugin({
|
new ModuleFederationPlugin({
|
||||||
name: 'demo',
|
name: 'demo',
|
||||||
filename: 'remoteEntry.js',
|
filename: 'remoteEntry.js',
|
||||||
exposes: {
|
exposes: getExposesConfig('demo'),
|
||||||
'./App': './src/App.tsx',
|
|
||||||
},
|
|
||||||
shared: {
|
shared: {
|
||||||
react: {
|
react: {
|
||||||
singleton: true,
|
singleton: true,
|
||||||
|
|||||||
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 { ModuleFederationPlugin } = require('webpack').container;
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
|
|
||||||
|
// Import the microfrontends registry
|
||||||
|
const { getExposesConfig } = require('../../web/src/microfrontends.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: 'development',
|
mode: 'development',
|
||||||
entry: './src/index.tsx',
|
entry: './src/index.tsx',
|
||||||
@ -39,9 +42,7 @@ module.exports = {
|
|||||||
new ModuleFederationPlugin({
|
new ModuleFederationPlugin({
|
||||||
name: 'faas',
|
name: 'faas',
|
||||||
filename: 'remoteEntry.js',
|
filename: 'remoteEntry.js',
|
||||||
exposes: {
|
exposes: getExposesConfig('faas'),
|
||||||
'./App': './src/App.tsx',
|
|
||||||
},
|
|
||||||
shared: {
|
shared: {
|
||||||
react: {
|
react: {
|
||||||
singleton: true,
|
singleton: true,
|
||||||
|
|||||||
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 HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
const { ModuleFederationPlugin } = require('webpack').container;
|
const { ModuleFederationPlugin } = require('webpack').container;
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
|
|
||||||
|
// Import the microfrontends registry
|
||||||
|
const { getExposesConfig } = require('../../web/src/microfrontends.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: 'development',
|
mode: 'development',
|
||||||
entry: './src/index.tsx',
|
entry: './src/index.tsx',
|
||||||
@ -39,9 +43,7 @@ module.exports = {
|
|||||||
new ModuleFederationPlugin({
|
new ModuleFederationPlugin({
|
||||||
name: 'kms',
|
name: 'kms',
|
||||||
filename: 'remoteEntry.js',
|
filename: 'remoteEntry.js',
|
||||||
exposes: {
|
exposes: getExposesConfig('kms'),
|
||||||
'./App': './src/App.tsx',
|
|
||||||
},
|
|
||||||
shared: {
|
shared: {
|
||||||
react: {
|
react: {
|
||||||
singleton: true,
|
singleton: true,
|
||||||
|
|||||||
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 { Loader, Center, Text, Stack, Alert } from '@mantine/core';
|
||||||
import { IconAlertCircle } from '@tabler/icons-react';
|
import { IconAlertCircle } from '@tabler/icons-react';
|
||||||
import Breadcrumbs from './Breadcrumbs';
|
import Breadcrumbs from './Breadcrumbs';
|
||||||
|
import { microFrontends } from '../microfrontends.js';
|
||||||
|
|
||||||
const DemoApp = React.lazy(() => import('demo/App'));
|
// Pre-defined lazy imports for each microfrontend
|
||||||
const KMSApp = React.lazy(() => import('kms/App'));
|
// @ts-ignore - These modules are loaded at runtime via Module Federation
|
||||||
const FaaSApp = React.lazy(() => import('faas/App'));
|
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 AppLoader: React.FC = () => {
|
||||||
const { appName } = useParams<{ appName: string }>();
|
const { appName } = useParams<{ appName: string }>();
|
||||||
@ -32,32 +44,21 @@ const AppLoader: React.FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const renderApp = () => {
|
const renderApp = () => {
|
||||||
switch (appName) {
|
// Check if the app is registered in our microfrontends registry
|
||||||
case 'demo':
|
if (appName && appComponents[appName] && microFrontends[appName]) {
|
||||||
|
const App = appComponents[appName];
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<LoadingFallback />}>
|
<Suspense fallback={<LoadingFallback />}>
|
||||||
<DemoApp />
|
<App />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
case 'kms':
|
}
|
||||||
return (
|
|
||||||
<Suspense fallback={<LoadingFallback />}>
|
|
||||||
<KMSApp />
|
|
||||||
</Suspense>
|
|
||||||
);
|
|
||||||
case 'faas':
|
|
||||||
return (
|
|
||||||
<Suspense fallback={<LoadingFallback />}>
|
|
||||||
<FaaSApp />
|
|
||||||
</Suspense>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return (
|
return (
|
||||||
<ErrorFallback
|
<ErrorFallback
|
||||||
error={`Application "${appName}" is not available or not configured.`}
|
error={`Application "${appName}" is not available or not configured.`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,44 +1,89 @@
|
|||||||
import React from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { NavLink, Stack, Text } from '@mantine/core';
|
import { NavLink, Stack, Text } from '@mantine/core';
|
||||||
import { useNavigate, useLocation } from 'react-router-dom';
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
IconDashboard,
|
IconDashboard,
|
||||||
IconKey,
|
IconKey,
|
||||||
IconFunction,
|
IconFunction,
|
||||||
|
IconApps,
|
||||||
} from '@tabler/icons-react';
|
} 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 Navigation: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
// Define all available applications
|
// Load favorites from localStorage (same as HomePage)
|
||||||
const allAvailableApplications = [
|
const [favoriteApps, setFavoriteApps] = useState<string[]>(() => {
|
||||||
{
|
try {
|
||||||
label: 'Demo App',
|
const saved = localStorage.getItem('favoriteMicrofrontends');
|
||||||
icon: IconDashboard,
|
return saved ? JSON.parse(saved) : ['demo'];
|
||||||
path: '/app/demo',
|
} catch {
|
||||||
},
|
return ['demo'];
|
||||||
{
|
}
|
||||||
label: 'Key Management',
|
});
|
||||||
icon: IconKey,
|
|
||||||
path: '/app/kms',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Functions',
|
|
||||||
icon: IconFunction,
|
|
||||||
path: '/app/faas',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Define which apps are favorited (you could make this dynamic later)
|
// Listen for changes to favorites in localStorage (in case they change in HomePage)
|
||||||
const favoritePaths = ['/app/demo'];
|
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 =>
|
const favoriteApplications = allAvailableApplications.filter(app =>
|
||||||
favoritePaths.includes(app.path)
|
favoriteApps.includes(app.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
const nonFavoriteApplications = allAvailableApplications.filter(app =>
|
const nonFavoriteApplications = allAvailableApplications.filter(app =>
|
||||||
!favoritePaths.includes(app.path)
|
!favoriteApps.includes(app.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
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 {
|
import {
|
||||||
SimpleGrid,
|
SimpleGrid,
|
||||||
Card,
|
Card,
|
||||||
@ -16,8 +16,10 @@ import {
|
|||||||
IconDashboard,
|
IconDashboard,
|
||||||
IconKey,
|
IconKey,
|
||||||
IconFunction,
|
IconFunction,
|
||||||
|
IconApps,
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import Breadcrumbs from '../components/Breadcrumbs';
|
import Breadcrumbs from '../components/Breadcrumbs';
|
||||||
|
import { microFrontends } from '../microfrontends.js';
|
||||||
|
|
||||||
interface App {
|
interface App {
|
||||||
id: string;
|
id: string;
|
||||||
@ -28,36 +30,71 @@ interface App {
|
|||||||
category: string;
|
category: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const HomePage: React.FC = () => {
|
// Map microfrontend IDs to display information
|
||||||
const navigate = useNavigate();
|
const getAppInfo = (id: string) => {
|
||||||
const [favoriteApps, setFavoriteApps] = useState<string[]>(['demo']);
|
const appInfo: Record<string, { name: string; description: string; icon: React.ComponentType<any>; category: string }> = {
|
||||||
|
demo: {
|
||||||
const availableApps: App[] = [
|
|
||||||
{
|
|
||||||
id: 'demo',
|
|
||||||
name: 'Demo App',
|
name: 'Demo App',
|
||||||
description: 'Sample microfrontend application for testing module federation',
|
description: 'Sample microfrontend application for testing module federation',
|
||||||
icon: IconDashboard,
|
icon: IconDashboard,
|
||||||
path: '/app/demo',
|
|
||||||
category: 'Development',
|
category: 'Development',
|
||||||
},
|
},
|
||||||
{
|
kms: {
|
||||||
id: 'kms',
|
|
||||||
name: 'Key Management System',
|
name: 'Key Management System',
|
||||||
description: 'Manage API keys, tokens, and access permissions for applications',
|
description: 'Manage API keys, tokens, and access permissions for applications',
|
||||||
icon: IconKey,
|
icon: IconKey,
|
||||||
path: '/app/kms',
|
|
||||||
category: 'Security',
|
category: 'Security',
|
||||||
},
|
},
|
||||||
{
|
faas: {
|
||||||
id: 'faas',
|
|
||||||
name: 'Function-as-a-Service',
|
name: 'Function-as-a-Service',
|
||||||
description: 'Deploy and manage serverless functions',
|
description: 'Deploy and manage serverless functions',
|
||||||
icon: IconFunction,
|
icon: IconFunction,
|
||||||
path: '/app/faas',
|
|
||||||
category: 'Development',
|
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) => {
|
const toggleFavorite = (appId: string) => {
|
||||||
setFavoriteApps(prev =>
|
setFavoriteApps(prev =>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
const { ModuleFederationPlugin } = require('webpack').container;
|
const { ModuleFederationPlugin } = require('webpack').container;
|
||||||
|
const { getRemotesConfig } = require('./src/microfrontends.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: 'development',
|
mode: 'development',
|
||||||
@ -44,11 +45,7 @@ module.exports = {
|
|||||||
plugins: [
|
plugins: [
|
||||||
new ModuleFederationPlugin({
|
new ModuleFederationPlugin({
|
||||||
name: 'shell',
|
name: 'shell',
|
||||||
remotes: {
|
remotes: getRemotesConfig(),
|
||||||
demo: 'demo@http://localhost:3001/remoteEntry.js',
|
|
||||||
kms: 'kms@http://localhost:3002/remoteEntry.js',
|
|
||||||
faas: 'faas@http://localhost:3003/remoteEntry.js',
|
|
||||||
},
|
|
||||||
shared: {
|
shared: {
|
||||||
react: {
|
react: {
|
||||||
singleton: true,
|
singleton: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user