github-to-mcp-monorepo 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/.env.example +8 -0
- package/.github/CODEOWNERS +6 -0
- package/.husky/pre-commit +1 -0
- package/.nvmrc +1 -0
- package/.prettierignore +5 -0
- package/.prettierrc +7 -0
- package/.vscode/settings.json +4 -0
- package/ARCHITECTURE.md +1429 -0
- package/CHANGELOG.md +167 -0
- package/CONTRIBUTING.md +327 -0
- package/LICENSE +201 -0
- package/README.md +1028 -0
- package/SECURITY.md +248 -0
- package/VISUAL_GUIDE.md +437 -0
- package/apps/vscode/IMPLEMENTATION.md +480 -0
- package/apps/vscode/README.md +248 -0
- package/apps/vscode/package.json +381 -0
- package/apps/vscode/resources/icon.png +0 -0
- package/apps/vscode/resources/icon.svg +5 -0
- package/apps/vscode/src/commands/browseRegistry.ts +211 -0
- package/apps/vscode/src/commands/configureClaudeDesktop.ts +332 -0
- package/apps/vscode/src/commands/convert.ts +82 -0
- package/apps/vscode/src/commands/convertCurrentRepo.ts +109 -0
- package/apps/vscode/src/commands/convertFromUrl.ts +138 -0
- package/apps/vscode/src/commands/index.ts +121 -0
- package/apps/vscode/src/commands/validate.ts +197 -0
- package/apps/vscode/src/extension.ts +464 -0
- package/apps/vscode/src/global.d.ts +36 -0
- package/apps/vscode/src/test/extension.test.ts +73 -0
- package/apps/vscode/src/utils/file-generator.ts +529 -0
- package/apps/vscode/src/utils/github-api.ts +335 -0
- package/apps/vscode/src/utils/index.ts +29 -0
- package/apps/vscode/src/utils/mcp-config.ts +334 -0
- package/apps/vscode/src/utils/storage.ts +87 -0
- package/apps/vscode/src/views/McpServersTreeView.ts +160 -0
- package/apps/vscode/src/views/OutputChannelView.ts +195 -0
- package/apps/vscode/src/views/StatusBarItem.ts +251 -0
- package/apps/vscode/src/views/ToolsExplorerView.ts +314 -0
- package/apps/vscode/src/views/historyProvider.ts +75 -0
- package/apps/vscode/src/views/index.ts +12 -0
- package/apps/vscode/src/views/resultsPanel.ts +330 -0
- package/apps/vscode/src/webviews/ConversionPanel.ts +350 -0
- package/apps/vscode/src/webviews/ToolDetailsPanel.ts +448 -0
- package/apps/vscode/src/webviews/index.ts +9 -0
- package/apps/vscode/src/webviews/webview-ui/styles.ts +492 -0
- package/apps/vscode/tsconfig.json +20 -0
- package/apps/web/PLAYGROUND_GUIDE.md +499 -0
- package/apps/web/README.md +505 -0
- package/apps/web/app/api/convert/route.ts +100 -0
- package/apps/web/app/api/convert/stream/route.ts +198 -0
- package/apps/web/app/api/deploy/route.ts +157 -0
- package/apps/web/app/api/edge/route.ts +308 -0
- package/apps/web/app/api/export-docker/route.ts +284 -0
- package/apps/web/app/api/generate-openapi/route.ts +119 -0
- package/apps/web/app/api/mcp/[serverId]/route.ts +263 -0
- package/apps/web/app/api/playground/connect/route.ts +143 -0
- package/apps/web/app/api/playground/disconnect/route.ts +78 -0
- package/apps/web/app/api/playground/execute/route.ts +135 -0
- package/apps/web/app/api/playground/sessions/route.ts +103 -0
- package/apps/web/app/api/playground/tools/route.ts +117 -0
- package/apps/web/app/api/playground/v2/connect/route.ts +96 -0
- package/apps/web/app/api/playground/v2/disconnect/route.ts +88 -0
- package/apps/web/app/api/playground/v2/health/route.ts +80 -0
- package/apps/web/app/api/playground/v2/prompts/route.ts +160 -0
- package/apps/web/app/api/playground/v2/resources/route.ts +159 -0
- package/apps/web/app/api/playground/v2/sessions/route.ts +184 -0
- package/apps/web/app/api/playground/v2/tools/route.ts +167 -0
- package/apps/web/app/api/stream/route.ts +232 -0
- package/apps/web/app/batch/BatchConvertClient.tsx +190 -0
- package/apps/web/app/batch/page.tsx +37 -0
- package/apps/web/app/convert/page.tsx +269 -0
- package/apps/web/app/dashboard/page.tsx +380 -0
- package/apps/web/app/globals.css +622 -0
- package/apps/web/app/layout.tsx +120 -0
- package/apps/web/app/manifest.ts +31 -0
- package/apps/web/app/opengraph-image.tsx +112 -0
- package/apps/web/app/page.old.tsx +924 -0
- package/apps/web/app/page.tsx +77 -0
- package/apps/web/app/playground/page.tsx +306 -0
- package/apps/web/app/playground/v2/error.tsx +163 -0
- package/apps/web/app/playground/v2/layout.tsx +58 -0
- package/apps/web/app/playground/v2/loading.tsx +152 -0
- package/apps/web/app/playground/v2/page.tsx +644 -0
- package/apps/web/app/playground/v2/providers.tsx +214 -0
- package/apps/web/app/playground/v2/use-shortcuts.ts +209 -0
- package/apps/web/app/playground/v2/use-url-state.ts +296 -0
- package/apps/web/app/providers.tsx +22 -0
- package/apps/web/app/sitemap.ts +32 -0
- package/apps/web/app/twitter-image.tsx +112 -0
- package/apps/web/components/BranchSelector.tsx +401 -0
- package/apps/web/components/ClaudeConfigExport.tsx +226 -0
- package/apps/web/components/Features.tsx +84 -0
- package/apps/web/components/Footer.tsx +119 -0
- package/apps/web/components/GenerationProgress.tsx +248 -0
- package/apps/web/components/GithubUrlInput.tsx +483 -0
- package/apps/web/components/Header.tsx +175 -0
- package/apps/web/components/Hero.tsx +117 -0
- package/apps/web/components/HowItWorks.tsx +119 -0
- package/apps/web/components/InstallBanner.tsx +158 -0
- package/apps/web/components/Logo.tsx +116 -0
- package/apps/web/components/ParticleBackground.tsx +105 -0
- package/apps/web/components/Playground.tsx +472 -0
- package/apps/web/components/PlaygroundToolTester.tsx +410 -0
- package/apps/web/components/ProductCards.tsx +179 -0
- package/apps/web/components/SplitView.tsx +194 -0
- package/apps/web/components/ToolFilter.tsx +260 -0
- package/apps/web/components/ToolList.tsx +325 -0
- package/apps/web/components/batch/BatchConvert.tsx +785 -0
- package/apps/web/components/batch/index.ts +7 -0
- package/apps/web/components/convert/ConfigTabs.tsx +230 -0
- package/apps/web/components/convert/ConversionResult.tsx +482 -0
- package/apps/web/components/convert/InlinePlayground.tsx +259 -0
- package/apps/web/components/convert/LoadingSteps.tsx +311 -0
- package/apps/web/components/convert/OneClickInstall.tsx +224 -0
- package/apps/web/components/convert/ToolCard.tsx +189 -0
- package/apps/web/components/convert/TryInPlayground.tsx +242 -0
- package/apps/web/components/convert/index.ts +12 -0
- package/apps/web/components/deploy/DeployButton.tsx +369 -0
- package/apps/web/components/deploy/index.ts +7 -0
- package/apps/web/components/docker/DockerExport.tsx +690 -0
- package/apps/web/components/docker/index.ts +7 -0
- package/apps/web/components/install/OneClickInstall.tsx +676 -0
- package/apps/web/components/install/index.ts +7 -0
- package/apps/web/components/playground/CapabilityTabs.tsx +150 -0
- package/apps/web/components/playground/ConnectionStatusV2.tsx +322 -0
- package/apps/web/components/playground/EmptyStates.tsx +305 -0
- package/apps/web/components/playground/ExecutionLog.tsx +260 -0
- package/apps/web/components/playground/ExecutionLogV2.tsx +378 -0
- package/apps/web/components/playground/JsonViewer.tsx +388 -0
- package/apps/web/components/playground/PlaygroundLayout.tsx +244 -0
- package/apps/web/components/playground/PromptsPanel.tsx +385 -0
- package/apps/web/components/playground/ResourcesPanel.tsx +378 -0
- package/apps/web/components/playground/SchemaForm.tsx +477 -0
- package/apps/web/components/playground/ServerStatus.tsx +151 -0
- package/apps/web/components/playground/ShareButton.tsx +239 -0
- package/apps/web/components/playground/ToolsPanel.tsx +309 -0
- package/apps/web/components/playground/TransportConfigurator.tsx +563 -0
- package/apps/web/components/playground/index.ts +74 -0
- package/apps/web/components/playground/types.ts +202 -0
- package/apps/web/components/streaming/StreamingProgress.tsx +441 -0
- package/apps/web/components/streaming/index.ts +7 -0
- package/apps/web/components/ui/badge.tsx +42 -0
- package/apps/web/components/ui/button.tsx +88 -0
- package/apps/web/components/ui/card.tsx +75 -0
- package/apps/web/components/ui/code-block.tsx +122 -0
- package/apps/web/components/ui/index.ts +12 -0
- package/apps/web/components/ui/input.tsx +55 -0
- package/apps/web/components/ui/tabs.tsx +61 -0
- package/apps/web/hooks/index.ts +85 -0
- package/apps/web/hooks/types.ts +1173 -0
- package/apps/web/hooks/use-conversion.ts +133 -0
- package/apps/web/hooks/use-execution-history.ts +376 -0
- package/apps/web/hooks/use-generation-progress.ts +147 -0
- package/apps/web/hooks/use-local-storage.ts +88 -0
- package/apps/web/hooks/use-mcp-client.ts +623 -0
- package/apps/web/hooks/use-mcp-connection.ts +500 -0
- package/apps/web/hooks/use-mcp-execution.ts +282 -0
- package/apps/web/hooks/use-mcp-prompts.ts +441 -0
- package/apps/web/hooks/use-mcp-resources.ts +430 -0
- package/apps/web/hooks/use-mcp-tools.ts +540 -0
- package/apps/web/hooks/use-playground-store.ts +299 -0
- package/apps/web/hooks/use-playground.ts +184 -0
- package/apps/web/hooks/use-streaming-conversion.ts +227 -0
- package/apps/web/hooks/useBatchConversion.ts +271 -0
- package/apps/web/hooks/useDockerConfig.ts +161 -0
- package/apps/web/hooks/usePlatformDetection.ts +80 -0
- package/apps/web/hooks/useStreaming.ts +199 -0
- package/apps/web/lib/api/errors.ts +386 -0
- package/apps/web/lib/api/index.ts +137 -0
- package/apps/web/lib/api/logger.ts +187 -0
- package/apps/web/lib/api/middleware.ts +364 -0
- package/apps/web/lib/api/openapi.ts +977 -0
- package/apps/web/lib/api/session-manager.ts +594 -0
- package/apps/web/lib/api/types.ts +433 -0
- package/apps/web/lib/api/validation.ts +523 -0
- package/apps/web/lib/constants.ts +114 -0
- package/apps/web/lib/mcp/client.ts +1137 -0
- package/apps/web/lib/mcp/events.ts +651 -0
- package/apps/web/lib/mcp/index.ts +347 -0
- package/apps/web/lib/mcp/logger.ts +428 -0
- package/apps/web/lib/mcp/metrics.ts +703 -0
- package/apps/web/lib/mcp/retry.ts +616 -0
- package/apps/web/lib/mcp/session-manager.ts +779 -0
- package/apps/web/lib/mcp/transports.ts +988 -0
- package/apps/web/lib/mcp/types.ts +594 -0
- package/apps/web/lib/mcp-client-enhanced.ts +871 -0
- package/apps/web/lib/mcp-client.ts +778 -0
- package/apps/web/lib/mcp-errors.ts +489 -0
- package/apps/web/lib/mcp-sandbox.ts +593 -0
- package/apps/web/lib/mcp-testing.ts +428 -0
- package/apps/web/lib/mcp-types.ts +448 -0
- package/apps/web/lib/playground-store.tsx +1147 -0
- package/apps/web/lib/utils.ts +439 -0
- package/apps/web/next-env.d.ts +5 -0
- package/apps/web/next.config.js +23 -0
- package/apps/web/package.json +55 -0
- package/apps/web/postcss.config.js +6 -0
- package/apps/web/public/.well-known/ai-plugin.json +17 -0
- package/apps/web/public/logo.svg +6 -0
- package/apps/web/public/robots.txt +22 -0
- package/apps/web/public/schema.json +27 -0
- package/apps/web/tailwind.config.js +26 -0
- package/apps/web/tailwind.config.ts +123 -0
- package/apps/web/tsconfig.json +20 -0
- package/apps/web/types/deploy.ts +139 -0
- package/apps/web/types/index.ts +247 -0
- package/apps/web/vercel.json +39 -0
- package/eslint.config.mjs +23 -0
- package/llms.txt +102 -0
- package/mkdocs/docs/api/core.md +318 -0
- package/mkdocs/docs/api/index.md +128 -0
- package/mkdocs/docs/api/mcp-server.md +301 -0
- package/mkdocs/docs/api/openapi-parser.md +254 -0
- package/mkdocs/docs/assets/logo.svg +7 -0
- package/mkdocs/docs/changelog.md +118 -0
- package/mkdocs/docs/cli/generate.md +148 -0
- package/mkdocs/docs/cli/index.md +52 -0
- package/mkdocs/docs/cli/inspect.md +164 -0
- package/mkdocs/docs/cli/serve.md +136 -0
- package/mkdocs/docs/concepts/classification.md +254 -0
- package/mkdocs/docs/concepts/how-it-works.md +299 -0
- package/mkdocs/docs/concepts/index.md +77 -0
- package/mkdocs/docs/concepts/mcp-protocol.md +362 -0
- package/mkdocs/docs/concepts/tool-types.md +382 -0
- package/mkdocs/docs/contributing/architecture.md +262 -0
- package/mkdocs/docs/contributing/development.md +245 -0
- package/mkdocs/docs/contributing/index.md +73 -0
- package/mkdocs/docs/contributing/testing.md +320 -0
- package/mkdocs/docs/getting-started/configuration.md +235 -0
- package/mkdocs/docs/getting-started/index.md +54 -0
- package/mkdocs/docs/getting-started/installation.md +145 -0
- package/mkdocs/docs/getting-started/quickstart.md +160 -0
- package/mkdocs/docs/guides/batch.md +375 -0
- package/mkdocs/docs/guides/claude-desktop.md +227 -0
- package/mkdocs/docs/guides/cursor.md +188 -0
- package/mkdocs/docs/guides/custom-tools.md +367 -0
- package/mkdocs/docs/guides/index.md +78 -0
- package/mkdocs/docs/guides/private-repos.md +221 -0
- package/mkdocs/docs/guides/vscode.md +247 -0
- package/mkdocs/docs/index.md +175 -0
- package/mkdocs/docs/reference/config.md +223 -0
- package/mkdocs/docs/reference/env.md +192 -0
- package/mkdocs/docs/reference/index.md +102 -0
- package/mkdocs/docs/reference/tools.md +309 -0
- package/mkdocs/docs/stylesheets/extra.css +231 -0
- package/mkdocs/mkdocs.yml +204 -0
- package/mkdocs/overrides/.gitkeep +1 -0
- package/mkdocs/overrides/main.html +7 -0
- package/mkdocs/python-deps.txt +7 -0
- package/mkdocs/vercel.json +11 -0
- package/package.json +63 -0
- package/packages/core/package.json +61 -0
- package/packages/core/src/__tests__/bitbucket-client.test.ts +366 -0
- package/packages/core/src/__tests__/cli.test.ts +235 -0
- package/packages/core/src/__tests__/code-extractor.test.ts +378 -0
- package/packages/core/src/__tests__/docker-generator.test.ts +255 -0
- package/packages/core/src/__tests__/github-client.test.ts +390 -0
- package/packages/core/src/__tests__/gitlab-client.test.ts +319 -0
- package/packages/core/src/__tests__/go-extractor.test.ts +351 -0
- package/packages/core/src/__tests__/graphql-extractor.test.ts +330 -0
- package/packages/core/src/__tests__/java-extractor.test.ts +497 -0
- package/packages/core/src/__tests__/plugins.test.ts +467 -0
- package/packages/core/src/__tests__/readme-extractor.test.ts +258 -0
- package/packages/core/src/__tests__/redis-cache.test.ts +307 -0
- package/packages/core/src/__tests__/rust-extractor.test.ts +252 -0
- package/packages/core/src/__tests__/streaming.test.ts +251 -0
- package/packages/core/src/additional-extractors.ts +333 -0
- package/packages/core/src/cache/cache-interface.ts +179 -0
- package/packages/core/src/cache/index.ts +210 -0
- package/packages/core/src/cache/redis-cache.ts +291 -0
- package/packages/core/src/cache/upstash-cache.ts +379 -0
- package/packages/core/src/cache.ts +251 -0
- package/packages/core/src/cli.ts +822 -0
- package/packages/core/src/code-extractor.ts +696 -0
- package/packages/core/src/docker-generator.ts +470 -0
- package/packages/core/src/edge-compatible.ts +491 -0
- package/packages/core/src/extractors/go-extractor.ts +791 -0
- package/packages/core/src/extractors/index.ts +9 -0
- package/packages/core/src/extractors/java-extractor.ts +937 -0
- package/packages/core/src/extractors/rust-extractor.ts +744 -0
- package/packages/core/src/github-client.ts +319 -0
- package/packages/core/src/go-generator.ts +356 -0
- package/packages/core/src/graphql-extractor.ts +358 -0
- package/packages/core/src/index.ts +797 -0
- package/packages/core/src/langchain-exporter.ts +617 -0
- package/packages/core/src/language-parsers.ts +1114 -0
- package/packages/core/src/mcp-introspector.ts +279 -0
- package/packages/core/src/monorepo-detector.ts +378 -0
- package/packages/core/src/plugins/index.ts +370 -0
- package/packages/core/src/plugins/registry.ts +404 -0
- package/packages/core/src/plugins/types.ts +215 -0
- package/packages/core/src/providers/base-provider.ts +246 -0
- package/packages/core/src/providers/bitbucket-client.ts +464 -0
- package/packages/core/src/providers/gitlab-client.ts +388 -0
- package/packages/core/src/providers/index.ts +176 -0
- package/packages/core/src/python-generator.ts +260 -0
- package/packages/core/src/queue/index.ts +100 -0
- package/packages/core/src/queue/memory-queue.ts +445 -0
- package/packages/core/src/queue/redis-queue.ts +578 -0
- package/packages/core/src/queue/types.ts +251 -0
- package/packages/core/src/readme-extractor.ts +409 -0
- package/packages/core/src/schema-generator.ts +638 -0
- package/packages/core/src/streaming.ts +999 -0
- package/packages/core/src/types.ts +289 -0
- package/packages/core/tsconfig.json +9 -0
- package/packages/core/tsup.config.ts +25 -0
- package/packages/mcp-server/README.md +297 -0
- package/packages/mcp-server/package.json +55 -0
- package/packages/mcp-server/src/__tests__/mcp-server.test.ts +177 -0
- package/packages/mcp-server/src/__tests__/tools.test.ts +217 -0
- package/packages/mcp-server/src/index.ts +1206 -0
- package/packages/mcp-server/src/prompts/index.ts +601 -0
- package/packages/mcp-server/src/tools/export-docker.ts +362 -0
- package/packages/mcp-server/src/tools/generate-openapi.ts +162 -0
- package/packages/mcp-server/src/tools/monitor-mcp-server.ts +448 -0
- package/packages/mcp-server/src/tools/stream-convert.ts +398 -0
- package/packages/mcp-server/src/tools/test-mcp-tool.ts +531 -0
- package/packages/mcp-server/tsconfig.json +12 -0
- package/packages/mcp-server/tsup.config.ts +14 -0
- package/packages/openapi-parser/package-lock.json +3028 -0
- package/packages/openapi-parser/package.json +41 -0
- package/packages/openapi-parser/src/analyzer.ts +700 -0
- package/packages/openapi-parser/src/asyncapi-parser.ts +475 -0
- package/packages/openapi-parser/src/cli.ts +302 -0
- package/packages/openapi-parser/src/generator.ts +570 -0
- package/packages/openapi-parser/src/generators/express-analyzer.ts +649 -0
- package/packages/openapi-parser/src/generators/fastapi-analyzer.ts +960 -0
- package/packages/openapi-parser/src/generators/index.ts +200 -0
- package/packages/openapi-parser/src/generators/nextjs-analyzer.ts +768 -0
- package/packages/openapi-parser/src/generators/openapi-builder.ts +527 -0
- package/packages/openapi-parser/src/generators/types.ts +298 -0
- package/packages/openapi-parser/src/graphql-parser.ts +462 -0
- package/packages/openapi-parser/src/grpc-parser.ts +649 -0
- package/packages/openapi-parser/src/har-parser.ts +723 -0
- package/packages/openapi-parser/src/index.ts +635 -0
- package/packages/openapi-parser/src/insomnia-parser.ts +614 -0
- package/packages/openapi-parser/src/parser.ts +231 -0
- package/packages/openapi-parser/src/postman-parser.ts +611 -0
- package/packages/openapi-parser/src/ref-resolver.ts +313 -0
- package/packages/openapi-parser/src/transformer.ts +459 -0
- package/packages/openapi-parser/tests/generators/express.test.ts +209 -0
- package/packages/openapi-parser/tests/generators/fastapi.test.ts +236 -0
- package/packages/openapi-parser/tests/generators/nextjs.test.ts +273 -0
- package/packages/openapi-parser/tests/parsers.test.ts +847 -0
- package/packages/openapi-parser/tsconfig.json +9 -0
- package/packages/openapi-parser/tsup.config.ts +11 -0
- package/packages/registry/package.json +59 -0
- package/packages/registry/src/cli.ts +456 -0
- package/packages/registry/src/index.ts +44 -0
- package/packages/registry/src/popular/github.json +47 -0
- package/packages/registry/src/popular/index.ts +55 -0
- package/packages/registry/src/popular/linear.json +42 -0
- package/packages/registry/src/popular/notion.json +42 -0
- package/packages/registry/src/popular/openai.json +40 -0
- package/packages/registry/src/popular/resend.json +38 -0
- package/packages/registry/src/popular/slack.json +42 -0
- package/packages/registry/src/popular/stripe.json +163 -0
- package/packages/registry/src/popular/supabase.json +42 -0
- package/packages/registry/src/popular/twilio.json +40 -0
- package/packages/registry/src/popular/vercel.json +40 -0
- package/packages/registry/src/registry.ts +492 -0
- package/packages/registry/src/storage.ts +334 -0
- package/packages/registry/src/types.ts +275 -0
- package/packages/registry/src/updater.ts +208 -0
- package/packages/registry/tsconfig.json +10 -0
- package/packages/registry/tsup.config.ts +11 -0
- package/pnpm-workspace.yaml +3 -0
- package/scripts/build-docs.sh +16 -0
- package/server.json +9 -0
- package/templates/Dockerfile.python.template +60 -0
- package/templates/Dockerfile.typescript.template +60 -0
- package/templates/docker-compose.template.yml +68 -0
- package/tests/fixtures/express-app/index.js +34 -0
- package/tests/fixtures/express-app/routes/posts.js +43 -0
- package/tests/fixtures/express-app/routes/users.js +58 -0
- package/tests/fixtures/fastapi-app/main.py +125 -0
- package/tests/fixtures/fastapi-app/routes/admin.py +42 -0
- package/tests/fixtures/graphql/simple-schema.graphql +65 -0
- package/tests/fixtures/mocks/github-api-responses.json +63 -0
- package/tests/fixtures/nextjs-app/app/api/posts/route.ts +55 -0
- package/tests/fixtures/nextjs-app/app/api/users/[id]/route.ts +63 -0
- package/tests/fixtures/nextjs-app/app/api/users/route.ts +44 -0
- package/tests/fixtures/nextjs-app/pages/api/health.ts +28 -0
- package/tests/fixtures/openapi/petstore.yaml +179 -0
- package/tests/integration/langchain-export.test.ts +405 -0
- package/tests/integration/openapi-conversion.test.ts +221 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +32 -0
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Docker Export Component - Generate Dockerfile and docker-compose for MCP servers
|
|
3
|
+
* @copyright 2024-2026 nirholas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use client';
|
|
8
|
+
|
|
9
|
+
import { useState, useMemo, useCallback } from 'react';
|
|
10
|
+
import { motion, AnimatePresence } from 'framer-motion';
|
|
11
|
+
import {
|
|
12
|
+
Container,
|
|
13
|
+
Download,
|
|
14
|
+
Copy,
|
|
15
|
+
Check,
|
|
16
|
+
Settings,
|
|
17
|
+
Code,
|
|
18
|
+
FileText,
|
|
19
|
+
Terminal,
|
|
20
|
+
ChevronDown,
|
|
21
|
+
ChevronUp,
|
|
22
|
+
Play,
|
|
23
|
+
Package,
|
|
24
|
+
Shield,
|
|
25
|
+
Cpu,
|
|
26
|
+
HardDrive,
|
|
27
|
+
Network,
|
|
28
|
+
Eye,
|
|
29
|
+
EyeOff,
|
|
30
|
+
} from 'lucide-react';
|
|
31
|
+
import type { DockerConfig, DockerExportOptions, ConversionResult } from '@/types';
|
|
32
|
+
|
|
33
|
+
interface DockerExportProps {
|
|
34
|
+
result: ConversionResult;
|
|
35
|
+
serverName?: string;
|
|
36
|
+
onExport?: (config: DockerConfig) => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const BASE_IMAGES = [
|
|
40
|
+
{ value: 'node:20-alpine', label: 'Node.js 20 Alpine', size: '~180MB' },
|
|
41
|
+
{ value: 'node:20-slim', label: 'Node.js 20 Slim', size: '~240MB' },
|
|
42
|
+
{ value: 'node:20', label: 'Node.js 20', size: '~1GB' },
|
|
43
|
+
{ value: 'python:3.11-alpine', label: 'Python 3.11 Alpine', size: '~50MB' },
|
|
44
|
+
{ value: 'python:3.11-slim', label: 'Python 3.11 Slim', size: '~150MB' },
|
|
45
|
+
{ value: 'python:3.11', label: 'Python 3.11', size: '~900MB' },
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const DEFAULT_OPTIONS: DockerExportOptions = {
|
|
49
|
+
baseImage: 'node:20-alpine',
|
|
50
|
+
port: 3000,
|
|
51
|
+
exposePorts: [3000],
|
|
52
|
+
envVars: {},
|
|
53
|
+
volumes: [],
|
|
54
|
+
healthCheck: true,
|
|
55
|
+
multiStage: true,
|
|
56
|
+
runAsNonRoot: true,
|
|
57
|
+
labels: true,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default function DockerExport({
|
|
61
|
+
result,
|
|
62
|
+
serverName = 'mcp-server',
|
|
63
|
+
onExport,
|
|
64
|
+
}: DockerExportProps) {
|
|
65
|
+
const [options, setOptions] = useState<DockerExportOptions>(DEFAULT_OPTIONS);
|
|
66
|
+
const [activeTab, setActiveTab] = useState<'dockerfile' | 'compose' | 'env' | 'commands'>('dockerfile');
|
|
67
|
+
const [showAdvanced, setShowAdvanced] = useState(false);
|
|
68
|
+
const [copiedField, setCopiedField] = useState<string | null>(null);
|
|
69
|
+
const [showPreview, setShowPreview] = useState(true);
|
|
70
|
+
|
|
71
|
+
const sanitizedServerName = useMemo(() =>
|
|
72
|
+
serverName.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/--+/g, '-'),
|
|
73
|
+
[serverName]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Generate Dockerfile content
|
|
77
|
+
const dockerfile = useMemo(() => {
|
|
78
|
+
const isNode = options.baseImage.includes('node');
|
|
79
|
+
const isPython = options.baseImage.includes('python');
|
|
80
|
+
const isAlpine = options.baseImage.includes('alpine');
|
|
81
|
+
|
|
82
|
+
let content = `# ${sanitizedServerName} MCP Server
|
|
83
|
+
# Generated by github-to-mcp
|
|
84
|
+
# https://github-to-mcp.dev
|
|
85
|
+
|
|
86
|
+
`;
|
|
87
|
+
|
|
88
|
+
if (options.multiStage && isNode) {
|
|
89
|
+
content += `# Build stage
|
|
90
|
+
FROM ${options.baseImage} AS builder
|
|
91
|
+
|
|
92
|
+
WORKDIR /app
|
|
93
|
+
|
|
94
|
+
# Copy package files
|
|
95
|
+
COPY package*.json ./
|
|
96
|
+
|
|
97
|
+
# Install dependencies
|
|
98
|
+
RUN npm ci --only=production
|
|
99
|
+
|
|
100
|
+
# Copy source files
|
|
101
|
+
COPY . .
|
|
102
|
+
|
|
103
|
+
# Build if needed
|
|
104
|
+
RUN npm run build --if-present
|
|
105
|
+
|
|
106
|
+
# Production stage
|
|
107
|
+
FROM ${options.baseImage} AS production
|
|
108
|
+
|
|
109
|
+
`;
|
|
110
|
+
} else {
|
|
111
|
+
content += `FROM ${options.baseImage}
|
|
112
|
+
|
|
113
|
+
`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (options.labels) {
|
|
117
|
+
content += `# Labels
|
|
118
|
+
LABEL org.opencontainers.image.title="${sanitizedServerName}"
|
|
119
|
+
LABEL org.opencontainers.image.description="MCP Server with ${result.tools.length} tools"
|
|
120
|
+
LABEL org.opencontainers.image.source="${result.repository.url}"
|
|
121
|
+
LABEL org.opencontainers.image.vendor="github-to-mcp"
|
|
122
|
+
|
|
123
|
+
`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
content += `WORKDIR /app
|
|
127
|
+
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
// Install dependencies for Alpine
|
|
131
|
+
if (isAlpine && !options.multiStage) {
|
|
132
|
+
if (isNode) {
|
|
133
|
+
content += `# Install dependencies
|
|
134
|
+
RUN apk add --no-cache dumb-init
|
|
135
|
+
|
|
136
|
+
`;
|
|
137
|
+
} else if (isPython) {
|
|
138
|
+
content += `# Install dependencies
|
|
139
|
+
RUN apk add --no-cache gcc musl-dev libffi-dev
|
|
140
|
+
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Non-root user
|
|
146
|
+
if (options.runAsNonRoot) {
|
|
147
|
+
if (isAlpine) {
|
|
148
|
+
content += `# Create non-root user
|
|
149
|
+
RUN addgroup -g 1001 -S mcpuser && \\
|
|
150
|
+
adduser -S -D -H -u 1001 -s /sbin/nologin -G mcpuser mcpuser
|
|
151
|
+
|
|
152
|
+
`;
|
|
153
|
+
} else {
|
|
154
|
+
content += `# Create non-root user
|
|
155
|
+
RUN groupadd -g 1001 mcpuser && \\
|
|
156
|
+
useradd -r -u 1001 -g mcpuser mcpuser
|
|
157
|
+
|
|
158
|
+
`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Copy files
|
|
163
|
+
if (options.multiStage && isNode) {
|
|
164
|
+
content += `# Copy from builder
|
|
165
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
166
|
+
COPY --from=builder /app/dist ./dist
|
|
167
|
+
COPY --from=builder /app/package.json ./
|
|
168
|
+
|
|
169
|
+
`;
|
|
170
|
+
} else if (isNode) {
|
|
171
|
+
content += `# Copy package files and install
|
|
172
|
+
COPY package*.json ./
|
|
173
|
+
RUN npm ci --only=production
|
|
174
|
+
|
|
175
|
+
# Copy application
|
|
176
|
+
COPY . .
|
|
177
|
+
|
|
178
|
+
`;
|
|
179
|
+
} else if (isPython) {
|
|
180
|
+
content += `# Copy requirements and install
|
|
181
|
+
COPY requirements.txt ./
|
|
182
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
183
|
+
|
|
184
|
+
# Copy application
|
|
185
|
+
COPY . .
|
|
186
|
+
|
|
187
|
+
`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Environment variables
|
|
191
|
+
if (Object.keys(options.envVars).length > 0) {
|
|
192
|
+
content += `# Environment variables\n`;
|
|
193
|
+
Object.entries(options.envVars).forEach(([key, value]) => {
|
|
194
|
+
content += `ENV ${key}=${value}\n`;
|
|
195
|
+
});
|
|
196
|
+
content += '\n';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
content += `# Set environment
|
|
200
|
+
ENV NODE_ENV=production
|
|
201
|
+
ENV PORT=${options.port}
|
|
202
|
+
|
|
203
|
+
`;
|
|
204
|
+
|
|
205
|
+
// Expose ports
|
|
206
|
+
if (options.exposePorts.length > 0) {
|
|
207
|
+
content += `# Expose ports\n`;
|
|
208
|
+
options.exposePorts.forEach(port => {
|
|
209
|
+
content += `EXPOSE ${port}\n`;
|
|
210
|
+
});
|
|
211
|
+
content += '\n';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Health check
|
|
215
|
+
if (options.healthCheck) {
|
|
216
|
+
content += `# Health check
|
|
217
|
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \\
|
|
218
|
+
CMD ${isNode ? 'node -e "require(\'http\').get(\'http://localhost:' + options.port + '/health\', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"' : 'curl -f http://localhost:' + options.port + '/health || exit 1'}
|
|
219
|
+
|
|
220
|
+
`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Set user
|
|
224
|
+
if (options.runAsNonRoot) {
|
|
225
|
+
content += `# Run as non-root user
|
|
226
|
+
USER mcpuser
|
|
227
|
+
|
|
228
|
+
`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Entrypoint
|
|
232
|
+
if (isNode) {
|
|
233
|
+
if (isAlpine) {
|
|
234
|
+
content += `# Start server
|
|
235
|
+
ENTRYPOINT ["dumb-init", "--"]
|
|
236
|
+
CMD ["node", "dist/index.js"]
|
|
237
|
+
`;
|
|
238
|
+
} else {
|
|
239
|
+
content += `# Start server
|
|
240
|
+
CMD ["node", "dist/index.js"]
|
|
241
|
+
`;
|
|
242
|
+
}
|
|
243
|
+
} else if (isPython) {
|
|
244
|
+
content += `# Start server
|
|
245
|
+
CMD ["python", "-m", "server"]
|
|
246
|
+
`;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return content;
|
|
250
|
+
}, [options, sanitizedServerName, result]);
|
|
251
|
+
|
|
252
|
+
// Generate docker-compose content
|
|
253
|
+
const dockerCompose = useMemo(() => {
|
|
254
|
+
let content = `# ${sanitizedServerName} Docker Compose
|
|
255
|
+
# Generated by github-to-mcp
|
|
256
|
+
|
|
257
|
+
version: '3.8'
|
|
258
|
+
|
|
259
|
+
services:
|
|
260
|
+
${sanitizedServerName}:
|
|
261
|
+
build:
|
|
262
|
+
context: .
|
|
263
|
+
dockerfile: Dockerfile
|
|
264
|
+
container_name: ${sanitizedServerName}
|
|
265
|
+
restart: unless-stopped
|
|
266
|
+
ports:
|
|
267
|
+
`;
|
|
268
|
+
|
|
269
|
+
options.exposePorts.forEach(port => {
|
|
270
|
+
content += ` - "${port}:${port}"\n`;
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
content += ` environment:
|
|
274
|
+
- NODE_ENV=production
|
|
275
|
+
`;
|
|
276
|
+
|
|
277
|
+
Object.entries(options.envVars).forEach(([key, value]) => {
|
|
278
|
+
content += ` - ${key}=${value}\n`;
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
if (options.volumes.length > 0) {
|
|
282
|
+
content += ` volumes:\n`;
|
|
283
|
+
options.volumes.forEach(vol => {
|
|
284
|
+
content += ` - ${vol}\n`;
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (options.healthCheck) {
|
|
289
|
+
content += ` healthcheck:
|
|
290
|
+
test: ["CMD", "curl", "-f", "http://localhost:${options.port}/health"]
|
|
291
|
+
interval: 30s
|
|
292
|
+
timeout: 10s
|
|
293
|
+
retries: 3
|
|
294
|
+
start_period: 10s
|
|
295
|
+
`;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
content += ` networks:
|
|
299
|
+
- mcp-network
|
|
300
|
+
|
|
301
|
+
networks:
|
|
302
|
+
mcp-network:
|
|
303
|
+
driver: bridge
|
|
304
|
+
`;
|
|
305
|
+
|
|
306
|
+
return content;
|
|
307
|
+
}, [options, sanitizedServerName]);
|
|
308
|
+
|
|
309
|
+
// Generate .env.example content
|
|
310
|
+
const envExample = useMemo(() => {
|
|
311
|
+
let content = `# ${sanitizedServerName} Environment Variables
|
|
312
|
+
# Copy this file to .env and fill in the values
|
|
313
|
+
|
|
314
|
+
# Server Configuration
|
|
315
|
+
PORT=${options.port}
|
|
316
|
+
NODE_ENV=production
|
|
317
|
+
|
|
318
|
+
# API Keys (if needed)
|
|
319
|
+
# OPENAI_API_KEY=sk-...
|
|
320
|
+
# ANTHROPIC_API_KEY=sk-ant-...
|
|
321
|
+
|
|
322
|
+
# Custom Environment Variables
|
|
323
|
+
`;
|
|
324
|
+
|
|
325
|
+
Object.entries(options.envVars).forEach(([key, value]) => {
|
|
326
|
+
content += `${key}=${value}\n`;
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
return content;
|
|
330
|
+
}, [options, sanitizedServerName]);
|
|
331
|
+
|
|
332
|
+
// Generate build/run commands
|
|
333
|
+
const commands = useMemo(() => ({
|
|
334
|
+
build: `docker build -t ${sanitizedServerName}:latest .`,
|
|
335
|
+
run: `docker run -d --name ${sanitizedServerName} -p ${options.port}:${options.port} ${sanitizedServerName}:latest`,
|
|
336
|
+
stop: `docker stop ${sanitizedServerName}`,
|
|
337
|
+
logs: `docker logs -f ${sanitizedServerName}`,
|
|
338
|
+
compose_up: `docker-compose up -d`,
|
|
339
|
+
compose_down: `docker-compose down`,
|
|
340
|
+
compose_logs: `docker-compose logs -f`,
|
|
341
|
+
}), [sanitizedServerName, options.port]);
|
|
342
|
+
|
|
343
|
+
const copyToClipboard = useCallback(async (text: string, field: string) => {
|
|
344
|
+
await navigator.clipboard.writeText(text);
|
|
345
|
+
setCopiedField(field);
|
|
346
|
+
setTimeout(() => setCopiedField(null), 2000);
|
|
347
|
+
}, []);
|
|
348
|
+
|
|
349
|
+
const downloadFile = useCallback((content: string, filename: string) => {
|
|
350
|
+
const blob = new Blob([content], { type: 'text/plain' });
|
|
351
|
+
const url = URL.createObjectURL(blob);
|
|
352
|
+
const a = document.createElement('a');
|
|
353
|
+
a.href = url;
|
|
354
|
+
a.download = filename;
|
|
355
|
+
document.body.appendChild(a);
|
|
356
|
+
a.click();
|
|
357
|
+
document.body.removeChild(a);
|
|
358
|
+
URL.revokeObjectURL(url);
|
|
359
|
+
}, []);
|
|
360
|
+
|
|
361
|
+
const downloadAll = useCallback(() => {
|
|
362
|
+
downloadFile(dockerfile, 'Dockerfile');
|
|
363
|
+
downloadFile(dockerCompose, 'docker-compose.yml');
|
|
364
|
+
downloadFile(envExample, '.env.example');
|
|
365
|
+
|
|
366
|
+
if (onExport) {
|
|
367
|
+
onExport({
|
|
368
|
+
dockerfile,
|
|
369
|
+
dockerCompose,
|
|
370
|
+
envExample,
|
|
371
|
+
buildCommand: commands.build,
|
|
372
|
+
runCommand: commands.run,
|
|
373
|
+
serverName: sanitizedServerName,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}, [dockerfile, dockerCompose, envExample, commands, downloadFile, onExport, sanitizedServerName]);
|
|
377
|
+
|
|
378
|
+
const TabButton = ({ id, label, icon: Icon }: { id: typeof activeTab; label: string; icon: typeof FileText }) => (
|
|
379
|
+
<button
|
|
380
|
+
onClick={() => setActiveTab(id)}
|
|
381
|
+
className={`flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg transition-colors ${
|
|
382
|
+
activeTab === id
|
|
383
|
+
? 'bg-white text-black'
|
|
384
|
+
: 'text-neutral-400 hover:text-white hover:bg-white/5'
|
|
385
|
+
}`}
|
|
386
|
+
>
|
|
387
|
+
<Icon className="w-4 h-4" />
|
|
388
|
+
{label}
|
|
389
|
+
</button>
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
const CopyButton = ({ text, field }: { text: string; field: string }) => (
|
|
393
|
+
<button
|
|
394
|
+
onClick={() => copyToClipboard(text, field)}
|
|
395
|
+
className="p-2 rounded-lg bg-white/10 hover:bg-white/20 transition-colors"
|
|
396
|
+
title="Copy to clipboard"
|
|
397
|
+
>
|
|
398
|
+
{copiedField === field ? (
|
|
399
|
+
<Check className="w-4 h-4 text-green-400" />
|
|
400
|
+
) : (
|
|
401
|
+
<Copy className="w-4 h-4" />
|
|
402
|
+
)}
|
|
403
|
+
</button>
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
return (
|
|
407
|
+
<div className="space-y-6">
|
|
408
|
+
{/* Header */}
|
|
409
|
+
<motion.div
|
|
410
|
+
initial={{ opacity: 0, y: 20 }}
|
|
411
|
+
animate={{ opacity: 1, y: 0 }}
|
|
412
|
+
className="flex items-center justify-between"
|
|
413
|
+
>
|
|
414
|
+
<div className="flex items-center gap-3">
|
|
415
|
+
<div className="w-12 h-12 rounded-xl bg-blue-500/10 border border-blue-500/20 flex items-center justify-center">
|
|
416
|
+
<Container className="w-6 h-6 text-blue-400" />
|
|
417
|
+
</div>
|
|
418
|
+
<div>
|
|
419
|
+
<h2 className="text-xl font-semibold text-white">Docker Export</h2>
|
|
420
|
+
<p className="text-sm text-neutral-400">
|
|
421
|
+
Generate production-ready Docker configuration
|
|
422
|
+
</p>
|
|
423
|
+
</div>
|
|
424
|
+
</div>
|
|
425
|
+
<button
|
|
426
|
+
onClick={downloadAll}
|
|
427
|
+
className="flex items-center gap-2 px-4 py-2 bg-white text-black rounded-lg font-medium hover:bg-neutral-200 transition-colors"
|
|
428
|
+
>
|
|
429
|
+
<Download className="w-4 h-4" />
|
|
430
|
+
Download All
|
|
431
|
+
</button>
|
|
432
|
+
</motion.div>
|
|
433
|
+
|
|
434
|
+
{/* Configuration Options */}
|
|
435
|
+
<motion.div
|
|
436
|
+
initial={{ opacity: 0, y: 20 }}
|
|
437
|
+
animate={{ opacity: 1, y: 0 }}
|
|
438
|
+
transition={{ delay: 0.1 }}
|
|
439
|
+
className="rounded-xl border border-neutral-800 bg-neutral-900/50 backdrop-blur-xl overflow-hidden"
|
|
440
|
+
>
|
|
441
|
+
<button
|
|
442
|
+
onClick={() => setShowAdvanced(!showAdvanced)}
|
|
443
|
+
className="w-full p-4 flex items-center justify-between hover:bg-white/5 transition-colors"
|
|
444
|
+
>
|
|
445
|
+
<div className="flex items-center gap-2">
|
|
446
|
+
<Settings className="w-5 h-5 text-neutral-400" />
|
|
447
|
+
<span className="font-medium text-white">Configuration Options</span>
|
|
448
|
+
</div>
|
|
449
|
+
{showAdvanced ? (
|
|
450
|
+
<ChevronUp className="w-5 h-5 text-neutral-400" />
|
|
451
|
+
) : (
|
|
452
|
+
<ChevronDown className="w-5 h-5 text-neutral-400" />
|
|
453
|
+
)}
|
|
454
|
+
</button>
|
|
455
|
+
|
|
456
|
+
<AnimatePresence>
|
|
457
|
+
{showAdvanced && (
|
|
458
|
+
<motion.div
|
|
459
|
+
initial={{ height: 0, opacity: 0 }}
|
|
460
|
+
animate={{ height: 'auto', opacity: 1 }}
|
|
461
|
+
exit={{ height: 0, opacity: 0 }}
|
|
462
|
+
className="border-t border-neutral-800"
|
|
463
|
+
>
|
|
464
|
+
<div className="p-4 grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
465
|
+
{/* Base Image */}
|
|
466
|
+
<div>
|
|
467
|
+
<label className="block text-sm font-medium text-neutral-300 mb-2">
|
|
468
|
+
<Package className="w-4 h-4 inline mr-1" />
|
|
469
|
+
Base Image
|
|
470
|
+
</label>
|
|
471
|
+
<select
|
|
472
|
+
value={options.baseImage}
|
|
473
|
+
onChange={(e) => setOptions(prev => ({ ...prev, baseImage: e.target.value }))}
|
|
474
|
+
className="w-full p-2 rounded-lg bg-black border border-neutral-700 text-white text-sm focus:outline-none focus:border-white/50"
|
|
475
|
+
>
|
|
476
|
+
{BASE_IMAGES.map(img => (
|
|
477
|
+
<option key={img.value} value={img.value}>
|
|
478
|
+
{img.label} ({img.size})
|
|
479
|
+
</option>
|
|
480
|
+
))}
|
|
481
|
+
</select>
|
|
482
|
+
</div>
|
|
483
|
+
|
|
484
|
+
{/* Port */}
|
|
485
|
+
<div>
|
|
486
|
+
<label className="block text-sm font-medium text-neutral-300 mb-2">
|
|
487
|
+
<Network className="w-4 h-4 inline mr-1" />
|
|
488
|
+
Port
|
|
489
|
+
</label>
|
|
490
|
+
<input
|
|
491
|
+
type="number"
|
|
492
|
+
value={options.port}
|
|
493
|
+
onChange={(e) => setOptions(prev => ({
|
|
494
|
+
...prev,
|
|
495
|
+
port: parseInt(e.target.value) || 3000,
|
|
496
|
+
exposePorts: [parseInt(e.target.value) || 3000]
|
|
497
|
+
}))}
|
|
498
|
+
className="w-full p-2 rounded-lg bg-black border border-neutral-700 text-white text-sm focus:outline-none focus:border-white/50"
|
|
499
|
+
min={1}
|
|
500
|
+
max={65535}
|
|
501
|
+
/>
|
|
502
|
+
</div>
|
|
503
|
+
|
|
504
|
+
{/* Toggle Options */}
|
|
505
|
+
<div className="md:col-span-2 grid grid-cols-2 md:grid-cols-4 gap-3">
|
|
506
|
+
{[
|
|
507
|
+
{ key: 'multiStage', label: 'Multi-stage Build', icon: Cpu },
|
|
508
|
+
{ key: 'healthCheck', label: 'Health Check', icon: Shield },
|
|
509
|
+
{ key: 'runAsNonRoot', label: 'Non-root User', icon: Shield },
|
|
510
|
+
{ key: 'labels', label: 'OCI Labels', icon: FileText },
|
|
511
|
+
].map(({ key, label, icon: Icon }) => (
|
|
512
|
+
<button
|
|
513
|
+
key={key}
|
|
514
|
+
onClick={() => setOptions(prev => ({ ...prev, [key]: !prev[key as keyof DockerExportOptions] }))}
|
|
515
|
+
className={`flex items-center gap-2 p-3 rounded-lg border transition-colors ${
|
|
516
|
+
options[key as keyof DockerExportOptions]
|
|
517
|
+
? 'bg-white/10 border-white/30 text-white'
|
|
518
|
+
: 'bg-black/30 border-neutral-700 text-neutral-400'
|
|
519
|
+
}`}
|
|
520
|
+
>
|
|
521
|
+
<Icon className="w-4 h-4" />
|
|
522
|
+
<span className="text-sm">{label}</span>
|
|
523
|
+
</button>
|
|
524
|
+
))}
|
|
525
|
+
</div>
|
|
526
|
+
</div>
|
|
527
|
+
</motion.div>
|
|
528
|
+
)}
|
|
529
|
+
</AnimatePresence>
|
|
530
|
+
</motion.div>
|
|
531
|
+
|
|
532
|
+
{/* File Tabs */}
|
|
533
|
+
<motion.div
|
|
534
|
+
initial={{ opacity: 0, y: 20 }}
|
|
535
|
+
animate={{ opacity: 1, y: 0 }}
|
|
536
|
+
transition={{ delay: 0.2 }}
|
|
537
|
+
className="rounded-xl border border-neutral-800 bg-neutral-900/50 backdrop-blur-xl overflow-hidden"
|
|
538
|
+
>
|
|
539
|
+
<div className="p-3 border-b border-neutral-800 flex items-center justify-between">
|
|
540
|
+
<div className="flex gap-2">
|
|
541
|
+
<TabButton id="dockerfile" label="Dockerfile" icon={FileText} />
|
|
542
|
+
<TabButton id="compose" label="Compose" icon={HardDrive} />
|
|
543
|
+
<TabButton id="env" label=".env" icon={Settings} />
|
|
544
|
+
<TabButton id="commands" label="Commands" icon={Terminal} />
|
|
545
|
+
</div>
|
|
546
|
+
<button
|
|
547
|
+
onClick={() => setShowPreview(!showPreview)}
|
|
548
|
+
className="p-2 rounded-lg bg-white/10 hover:bg-white/20 transition-colors"
|
|
549
|
+
title={showPreview ? 'Hide preview' : 'Show preview'}
|
|
550
|
+
>
|
|
551
|
+
{showPreview ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
|
|
552
|
+
</button>
|
|
553
|
+
</div>
|
|
554
|
+
|
|
555
|
+
<AnimatePresence mode="wait">
|
|
556
|
+
{showPreview && (
|
|
557
|
+
<motion.div
|
|
558
|
+
key={activeTab}
|
|
559
|
+
initial={{ opacity: 0, y: 10 }}
|
|
560
|
+
animate={{ opacity: 1, y: 0 }}
|
|
561
|
+
exit={{ opacity: 0, y: -10 }}
|
|
562
|
+
className="p-4"
|
|
563
|
+
>
|
|
564
|
+
{activeTab === 'dockerfile' && (
|
|
565
|
+
<div>
|
|
566
|
+
<div className="flex items-center justify-between mb-3">
|
|
567
|
+
<span className="text-sm text-neutral-400 font-mono">Dockerfile</span>
|
|
568
|
+
<div className="flex gap-2">
|
|
569
|
+
<CopyButton text={dockerfile} field="dockerfile" />
|
|
570
|
+
<button
|
|
571
|
+
onClick={() => downloadFile(dockerfile, 'Dockerfile')}
|
|
572
|
+
className="p-2 rounded-lg bg-white/10 hover:bg-white/20 transition-colors"
|
|
573
|
+
title="Download"
|
|
574
|
+
>
|
|
575
|
+
<Download className="w-4 h-4" />
|
|
576
|
+
</button>
|
|
577
|
+
</div>
|
|
578
|
+
</div>
|
|
579
|
+
<pre className="p-4 rounded-lg bg-black border border-neutral-800 overflow-x-auto text-sm font-mono text-neutral-300 max-h-96">
|
|
580
|
+
<code>{dockerfile}</code>
|
|
581
|
+
</pre>
|
|
582
|
+
</div>
|
|
583
|
+
)}
|
|
584
|
+
|
|
585
|
+
{activeTab === 'compose' && (
|
|
586
|
+
<div>
|
|
587
|
+
<div className="flex items-center justify-between mb-3">
|
|
588
|
+
<span className="text-sm text-neutral-400 font-mono">docker-compose.yml</span>
|
|
589
|
+
<div className="flex gap-2">
|
|
590
|
+
<CopyButton text={dockerCompose} field="compose" />
|
|
591
|
+
<button
|
|
592
|
+
onClick={() => downloadFile(dockerCompose, 'docker-compose.yml')}
|
|
593
|
+
className="p-2 rounded-lg bg-white/10 hover:bg-white/20 transition-colors"
|
|
594
|
+
title="Download"
|
|
595
|
+
>
|
|
596
|
+
<Download className="w-4 h-4" />
|
|
597
|
+
</button>
|
|
598
|
+
</div>
|
|
599
|
+
</div>
|
|
600
|
+
<pre className="p-4 rounded-lg bg-black border border-neutral-800 overflow-x-auto text-sm font-mono text-neutral-300 max-h-96">
|
|
601
|
+
<code>{dockerCompose}</code>
|
|
602
|
+
</pre>
|
|
603
|
+
</div>
|
|
604
|
+
)}
|
|
605
|
+
|
|
606
|
+
{activeTab === 'env' && (
|
|
607
|
+
<div>
|
|
608
|
+
<div className="flex items-center justify-between mb-3">
|
|
609
|
+
<span className="text-sm text-neutral-400 font-mono">.env.example</span>
|
|
610
|
+
<div className="flex gap-2">
|
|
611
|
+
<CopyButton text={envExample} field="env" />
|
|
612
|
+
<button
|
|
613
|
+
onClick={() => downloadFile(envExample, '.env.example')}
|
|
614
|
+
className="p-2 rounded-lg bg-white/10 hover:bg-white/20 transition-colors"
|
|
615
|
+
title="Download"
|
|
616
|
+
>
|
|
617
|
+
<Download className="w-4 h-4" />
|
|
618
|
+
</button>
|
|
619
|
+
</div>
|
|
620
|
+
</div>
|
|
621
|
+
<pre className="p-4 rounded-lg bg-black border border-neutral-800 overflow-x-auto text-sm font-mono text-neutral-300 max-h-96">
|
|
622
|
+
<code>{envExample}</code>
|
|
623
|
+
</pre>
|
|
624
|
+
</div>
|
|
625
|
+
)}
|
|
626
|
+
|
|
627
|
+
{activeTab === 'commands' && (
|
|
628
|
+
<div className="space-y-3">
|
|
629
|
+
{Object.entries(commands).map(([key, command]) => (
|
|
630
|
+
<div key={key} className="flex items-center gap-3">
|
|
631
|
+
<span className="text-xs text-neutral-500 w-24 flex-shrink-0">
|
|
632
|
+
{key.replace('_', ' ')}
|
|
633
|
+
</span>
|
|
634
|
+
<code className="flex-1 p-2 rounded bg-black border border-neutral-800 text-sm font-mono text-green-400">
|
|
635
|
+
{command}
|
|
636
|
+
</code>
|
|
637
|
+
<CopyButton text={command} field={key} />
|
|
638
|
+
</div>
|
|
639
|
+
))}
|
|
640
|
+
</div>
|
|
641
|
+
)}
|
|
642
|
+
</motion.div>
|
|
643
|
+
)}
|
|
644
|
+
</AnimatePresence>
|
|
645
|
+
</motion.div>
|
|
646
|
+
|
|
647
|
+
{/* Quick Start Guide */}
|
|
648
|
+
<motion.div
|
|
649
|
+
initial={{ opacity: 0, y: 20 }}
|
|
650
|
+
animate={{ opacity: 1, y: 0 }}
|
|
651
|
+
transition={{ delay: 0.3 }}
|
|
652
|
+
className="rounded-xl border border-neutral-800 bg-neutral-900/50 backdrop-blur-xl p-4"
|
|
653
|
+
>
|
|
654
|
+
<h3 className="text-sm font-medium text-white mb-3 flex items-center gap-2">
|
|
655
|
+
<Play className="w-4 h-4 text-green-400" />
|
|
656
|
+
Quick Start
|
|
657
|
+
</h3>
|
|
658
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
|
|
659
|
+
<div className="flex items-start gap-3">
|
|
660
|
+
<div className="w-6 h-6 rounded-full bg-white/10 flex items-center justify-center text-xs text-white flex-shrink-0">
|
|
661
|
+
1
|
|
662
|
+
</div>
|
|
663
|
+
<div>
|
|
664
|
+
<div className="text-neutral-300 font-medium">Download files</div>
|
|
665
|
+
<div className="text-neutral-500 text-xs">Click "Download All" button</div>
|
|
666
|
+
</div>
|
|
667
|
+
</div>
|
|
668
|
+
<div className="flex items-start gap-3">
|
|
669
|
+
<div className="w-6 h-6 rounded-full bg-white/10 flex items-center justify-center text-xs text-white flex-shrink-0">
|
|
670
|
+
2
|
|
671
|
+
</div>
|
|
672
|
+
<div>
|
|
673
|
+
<div className="text-neutral-300 font-medium">Build image</div>
|
|
674
|
+
<div className="text-neutral-500 text-xs font-mono">{commands.build}</div>
|
|
675
|
+
</div>
|
|
676
|
+
</div>
|
|
677
|
+
<div className="flex items-start gap-3">
|
|
678
|
+
<div className="w-6 h-6 rounded-full bg-white/10 flex items-center justify-center text-xs text-white flex-shrink-0">
|
|
679
|
+
3
|
|
680
|
+
</div>
|
|
681
|
+
<div>
|
|
682
|
+
<div className="text-neutral-300 font-medium">Run container</div>
|
|
683
|
+
<div className="text-neutral-500 text-xs font-mono">{commands.compose_up}</div>
|
|
684
|
+
</div>
|
|
685
|
+
</div>
|
|
686
|
+
</div>
|
|
687
|
+
</motion.div>
|
|
688
|
+
</div>
|
|
689
|
+
);
|
|
690
|
+
}
|