machinaos 0.0.1
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.template +71 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/bin/cli.js +159 -0
- package/client/.dockerignore +45 -0
- package/client/Dockerfile +68 -0
- package/client/eslint.config.js +29 -0
- package/client/index.html +13 -0
- package/client/nginx.conf +66 -0
- package/client/package.json +48 -0
- package/client/src/App.tsx +27 -0
- package/client/src/Dashboard.tsx +1173 -0
- package/client/src/ParameterPanel.tsx +301 -0
- package/client/src/components/AIAgentNode.tsx +321 -0
- package/client/src/components/APIKeyValidator.tsx +118 -0
- package/client/src/components/ClaudeChatModelNode.tsx +18 -0
- package/client/src/components/ConditionalEdge.tsx +189 -0
- package/client/src/components/CredentialsModal.tsx +306 -0
- package/client/src/components/EdgeConditionEditor.tsx +443 -0
- package/client/src/components/GeminiChatModelNode.tsx +18 -0
- package/client/src/components/GenericNode.tsx +357 -0
- package/client/src/components/LocationParameterPanel.tsx +154 -0
- package/client/src/components/ModelNode.tsx +286 -0
- package/client/src/components/OpenAIChatModelNode.tsx +18 -0
- package/client/src/components/OutputPanel.tsx +471 -0
- package/client/src/components/ParameterRenderer.tsx +1874 -0
- package/client/src/components/SkillEditorModal.tsx +417 -0
- package/client/src/components/SquareNode.tsx +797 -0
- package/client/src/components/StartNode.tsx +250 -0
- package/client/src/components/ToolkitNode.tsx +365 -0
- package/client/src/components/TriggerNode.tsx +463 -0
- package/client/src/components/auth/LoginPage.tsx +247 -0
- package/client/src/components/auth/ProtectedRoute.tsx +59 -0
- package/client/src/components/base/BaseChatModelNode.tsx +271 -0
- package/client/src/components/icons/AIProviderIcons.tsx +50 -0
- package/client/src/components/maps/GoogleMapsPicker.tsx +137 -0
- package/client/src/components/maps/MapsPreviewPanel.tsx +110 -0
- package/client/src/components/maps/index.ts +26 -0
- package/client/src/components/parameterPanel/InputSection.tsx +1094 -0
- package/client/src/components/parameterPanel/LocationPanelLayout.tsx +65 -0
- package/client/src/components/parameterPanel/MapsSection.tsx +92 -0
- package/client/src/components/parameterPanel/MiddleSection.tsx +571 -0
- package/client/src/components/parameterPanel/OutputSection.tsx +81 -0
- package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +82 -0
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -0
- package/client/src/components/parameterPanel/index.ts +42 -0
- package/client/src/components/shared/DataPanel.tsx +142 -0
- package/client/src/components/shared/JSONTreeRenderer.tsx +106 -0
- package/client/src/components/ui/AIResultModal.tsx +204 -0
- package/client/src/components/ui/AndroidSettingsPanel.tsx +401 -0
- package/client/src/components/ui/CodeEditor.tsx +81 -0
- package/client/src/components/ui/CollapsibleSection.tsx +88 -0
- package/client/src/components/ui/ComponentItem.tsx +154 -0
- package/client/src/components/ui/ComponentPalette.tsx +321 -0
- package/client/src/components/ui/ConsolePanel.tsx +1074 -0
- package/client/src/components/ui/ErrorBoundary.tsx +196 -0
- package/client/src/components/ui/InputNodesPanel.tsx +204 -0
- package/client/src/components/ui/MapSelector.tsx +314 -0
- package/client/src/components/ui/Modal.tsx +149 -0
- package/client/src/components/ui/NodeContextMenu.tsx +192 -0
- package/client/src/components/ui/NodeOutputPanel.tsx +1150 -0
- package/client/src/components/ui/OutputDisplayPanel.tsx +381 -0
- package/client/src/components/ui/SettingsPanel.tsx +243 -0
- package/client/src/components/ui/TopToolbar.tsx +736 -0
- package/client/src/components/ui/WhatsAppSettingsPanel.tsx +345 -0
- package/client/src/components/ui/WorkflowSidebar.tsx +294 -0
- package/client/src/config/antdTheme.ts +186 -0
- package/client/src/config/api.ts +54 -0
- package/client/src/contexts/AuthContext.tsx +221 -0
- package/client/src/contexts/ThemeContext.tsx +42 -0
- package/client/src/contexts/WebSocketContext.tsx +1971 -0
- package/client/src/factories/baseChatModelFactory.ts +256 -0
- package/client/src/hooks/useAndroidOperations.ts +164 -0
- package/client/src/hooks/useApiKeyValidation.ts +107 -0
- package/client/src/hooks/useApiKeys.ts +238 -0
- package/client/src/hooks/useAppTheme.ts +17 -0
- package/client/src/hooks/useComponentPalette.ts +51 -0
- package/client/src/hooks/useCopyPaste.ts +155 -0
- package/client/src/hooks/useDragAndDrop.ts +124 -0
- package/client/src/hooks/useDragVariable.ts +88 -0
- package/client/src/hooks/useExecution.ts +313 -0
- package/client/src/hooks/useParameterPanel.ts +176 -0
- package/client/src/hooks/useReactFlowNodes.ts +189 -0
- package/client/src/hooks/useToolSchema.ts +209 -0
- package/client/src/hooks/useWhatsApp.ts +196 -0
- package/client/src/hooks/useWorkflowManagement.ts +46 -0
- package/client/src/index.css +315 -0
- package/client/src/main.tsx +19 -0
- package/client/src/nodeDefinitions/aiAgentNodes.ts +336 -0
- package/client/src/nodeDefinitions/aiModelNodes.ts +340 -0
- package/client/src/nodeDefinitions/androidDeviceNodes.ts +140 -0
- package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -0
- package/client/src/nodeDefinitions/chatNodes.ts +135 -0
- package/client/src/nodeDefinitions/codeNodes.ts +54 -0
- package/client/src/nodeDefinitions/documentNodes.ts +379 -0
- package/client/src/nodeDefinitions/index.ts +15 -0
- package/client/src/nodeDefinitions/locationNodes.ts +463 -0
- package/client/src/nodeDefinitions/schedulerNodes.ts +220 -0
- package/client/src/nodeDefinitions/skillNodes.ts +211 -0
- package/client/src/nodeDefinitions/toolNodes.ts +198 -0
- package/client/src/nodeDefinitions/utilityNodes.ts +284 -0
- package/client/src/nodeDefinitions/whatsappNodes.ts +865 -0
- package/client/src/nodeDefinitions/workflowNodes.ts +41 -0
- package/client/src/nodeDefinitions.ts +104 -0
- package/client/src/schemas/workflowSchema.ts +264 -0
- package/client/src/services/dynamicParameterService.ts +96 -0
- package/client/src/services/execution/aiAgentExecutionService.ts +35 -0
- package/client/src/services/executionService.ts +232 -0
- package/client/src/services/workflowApi.ts +91 -0
- package/client/src/store/useAppStore.ts +582 -0
- package/client/src/styles/theme.ts +508 -0
- package/client/src/styles/zIndex.ts +17 -0
- package/client/src/types/ComponentTypes.ts +39 -0
- package/client/src/types/EdgeCondition.ts +231 -0
- package/client/src/types/INodeProperties.ts +288 -0
- package/client/src/types/NodeTypes.ts +28 -0
- package/client/src/utils/formatters.ts +33 -0
- package/client/src/utils/googleMapsLoader.ts +140 -0
- package/client/src/utils/locationUtils.ts +85 -0
- package/client/src/utils/nodeUtils.ts +31 -0
- package/client/src/utils/workflow.ts +30 -0
- package/client/src/utils/workflowExport.ts +120 -0
- package/client/src/vite-env.d.ts +12 -0
- package/client/tailwind.config.js +60 -0
- package/client/tsconfig.json +25 -0
- package/client/tsconfig.node.json +11 -0
- package/client/vite.config.js +35 -0
- package/docker-compose.prod.yml +107 -0
- package/docker-compose.yml +104 -0
- package/docs-MachinaOs/README.md +85 -0
- package/docs-MachinaOs/deployment/docker.mdx +228 -0
- package/docs-MachinaOs/deployment/production.mdx +345 -0
- package/docs-MachinaOs/docs.json +75 -0
- package/docs-MachinaOs/faq.mdx +309 -0
- package/docs-MachinaOs/favicon.svg +5 -0
- package/docs-MachinaOs/installation.mdx +160 -0
- package/docs-MachinaOs/introduction.mdx +114 -0
- package/docs-MachinaOs/logo/dark.svg +6 -0
- package/docs-MachinaOs/logo/light.svg +6 -0
- package/docs-MachinaOs/nodes/ai-agent.mdx +216 -0
- package/docs-MachinaOs/nodes/ai-models.mdx +240 -0
- package/docs-MachinaOs/nodes/android.mdx +411 -0
- package/docs-MachinaOs/nodes/overview.mdx +181 -0
- package/docs-MachinaOs/nodes/schedulers.mdx +316 -0
- package/docs-MachinaOs/nodes/webhooks.mdx +330 -0
- package/docs-MachinaOs/nodes/whatsapp.mdx +305 -0
- package/docs-MachinaOs/quickstart.mdx +119 -0
- package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +177 -0
- package/docs-MachinaOs/tutorials/android-automation.mdx +242 -0
- package/docs-MachinaOs/tutorials/first-workflow.mdx +134 -0
- package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +185 -0
- package/nul +0 -0
- package/package.json +70 -0
- package/scripts/build.js +158 -0
- package/scripts/check-ports.ps1 +33 -0
- package/scripts/clean.js +40 -0
- package/scripts/docker.js +93 -0
- package/scripts/kill-port.ps1 +154 -0
- package/scripts/start.js +210 -0
- package/scripts/stop.js +325 -0
- package/server/.dockerignore +44 -0
- package/server/Dockerfile +45 -0
- package/server/constants.py +249 -0
- package/server/core/__init__.py +1 -0
- package/server/core/cache.py +461 -0
- package/server/core/config.py +128 -0
- package/server/core/container.py +99 -0
- package/server/core/database.py +1211 -0
- package/server/core/logging.py +314 -0
- package/server/main.py +289 -0
- package/server/middleware/__init__.py +5 -0
- package/server/middleware/auth.py +89 -0
- package/server/models/__init__.py +1 -0
- package/server/models/auth.py +52 -0
- package/server/models/cache.py +24 -0
- package/server/models/database.py +211 -0
- package/server/models/nodes.py +455 -0
- package/server/package.json +9 -0
- package/server/pyproject.toml +72 -0
- package/server/requirements.txt +83 -0
- package/server/routers/__init__.py +1 -0
- package/server/routers/android.py +294 -0
- package/server/routers/auth.py +203 -0
- package/server/routers/database.py +151 -0
- package/server/routers/maps.py +142 -0
- package/server/routers/nodejs_compat.py +289 -0
- package/server/routers/webhook.py +90 -0
- package/server/routers/websocket.py +2127 -0
- package/server/routers/whatsapp.py +761 -0
- package/server/routers/workflow.py +200 -0
- package/server/services/__init__.py +1 -0
- package/server/services/ai.py +2415 -0
- package/server/services/android/__init__.py +27 -0
- package/server/services/android/broadcaster.py +114 -0
- package/server/services/android/client.py +608 -0
- package/server/services/android/manager.py +78 -0
- package/server/services/android/protocol.py +165 -0
- package/server/services/android_service.py +588 -0
- package/server/services/auth.py +131 -0
- package/server/services/chat_client.py +160 -0
- package/server/services/deployment/__init__.py +12 -0
- package/server/services/deployment/manager.py +706 -0
- package/server/services/deployment/state.py +47 -0
- package/server/services/deployment/triggers.py +275 -0
- package/server/services/event_waiter.py +785 -0
- package/server/services/execution/__init__.py +77 -0
- package/server/services/execution/cache.py +769 -0
- package/server/services/execution/conditions.py +373 -0
- package/server/services/execution/dlq.py +132 -0
- package/server/services/execution/executor.py +1351 -0
- package/server/services/execution/models.py +531 -0
- package/server/services/execution/recovery.py +235 -0
- package/server/services/handlers/__init__.py +126 -0
- package/server/services/handlers/ai.py +355 -0
- package/server/services/handlers/android.py +260 -0
- package/server/services/handlers/code.py +278 -0
- package/server/services/handlers/document.py +598 -0
- package/server/services/handlers/http.py +193 -0
- package/server/services/handlers/polyglot.py +105 -0
- package/server/services/handlers/tools.py +845 -0
- package/server/services/handlers/triggers.py +107 -0
- package/server/services/handlers/utility.py +822 -0
- package/server/services/handlers/whatsapp.py +476 -0
- package/server/services/maps.py +289 -0
- package/server/services/memory_store.py +103 -0
- package/server/services/node_executor.py +375 -0
- package/server/services/parameter_resolver.py +218 -0
- package/server/services/polyglot_client.py +169 -0
- package/server/services/scheduler.py +155 -0
- package/server/services/skill_loader.py +417 -0
- package/server/services/status_broadcaster.py +826 -0
- package/server/services/temporal/__init__.py +23 -0
- package/server/services/temporal/activities.py +344 -0
- package/server/services/temporal/client.py +76 -0
- package/server/services/temporal/executor.py +147 -0
- package/server/services/temporal/worker.py +251 -0
- package/server/services/temporal/workflow.py +355 -0
- package/server/services/temporal/ws_client.py +236 -0
- package/server/services/text.py +111 -0
- package/server/services/user_auth.py +172 -0
- package/server/services/websocket_client.py +29 -0
- package/server/services/workflow.py +597 -0
- package/server/skills/android-skill/SKILL.md +82 -0
- package/server/skills/assistant-personality/SKILL.md +45 -0
- package/server/skills/code-skill/SKILL.md +140 -0
- package/server/skills/http-skill/SKILL.md +161 -0
- package/server/skills/maps-skill/SKILL.md +170 -0
- package/server/skills/memory-skill/SKILL.md +154 -0
- package/server/skills/scheduler-skill/SKILL.md +84 -0
- package/server/skills/whatsapp-skill/SKILL.md +283 -0
- package/server/uv.lock +2916 -0
- package/server/whatsapp-rpc/.dockerignore +30 -0
- package/server/whatsapp-rpc/Dockerfile +44 -0
- package/server/whatsapp-rpc/Dockerfile.web +17 -0
- package/server/whatsapp-rpc/README.md +139 -0
- package/server/whatsapp-rpc/cli.js +95 -0
- package/server/whatsapp-rpc/configs/config.yaml +7 -0
- package/server/whatsapp-rpc/docker-compose.yml +35 -0
- package/server/whatsapp-rpc/docs/API.md +410 -0
- package/server/whatsapp-rpc/go.mod +67 -0
- package/server/whatsapp-rpc/go.sum +203 -0
- package/server/whatsapp-rpc/package.json +30 -0
- package/server/whatsapp-rpc/schema.json +1294 -0
- package/server/whatsapp-rpc/scripts/clean.cjs +66 -0
- package/server/whatsapp-rpc/scripts/cli.js +162 -0
- package/server/whatsapp-rpc/src/go/cmd/server/main.go +91 -0
- package/server/whatsapp-rpc/src/go/config/config.go +49 -0
- package/server/whatsapp-rpc/src/go/rpc/rpc.go +446 -0
- package/server/whatsapp-rpc/src/go/rpc/server.go +112 -0
- package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -0
- package/server/whatsapp-rpc/src/go/whatsapp/messages.go +390 -0
- package/server/whatsapp-rpc/src/go/whatsapp/service.go +2130 -0
- package/server/whatsapp-rpc/src/go/whatsapp/types.go +261 -0
- package/server/whatsapp-rpc/src/python/pyproject.toml +15 -0
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -0
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -0
- package/server/whatsapp-rpc/web/app.py +609 -0
- package/server/whatsapp-rpc/web/requirements.txt +6 -0
- package/server/whatsapp-rpc/web/rpc_client.py +427 -0
- package/server/whatsapp-rpc/web/static/openapi.yaml +59 -0
- package/server/whatsapp-rpc/web/templates/base.html +150 -0
- package/server/whatsapp-rpc/web/templates/contacts.html +240 -0
- package/server/whatsapp-rpc/web/templates/dashboard.html +320 -0
- package/server/whatsapp-rpc/web/templates/groups.html +328 -0
- package/server/whatsapp-rpc/web/templates/messages.html +465 -0
- package/server/whatsapp-rpc/web/templates/messaging.html +681 -0
- package/server/whatsapp-rpc/web/templates/send.html +259 -0
- package/server/whatsapp-rpc/web/templates/settings.html +459 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Google Maps API Loader - Singleton pattern to prevent multiple script loads
|
|
2
|
+
// Ensures Google Maps JavaScript API is loaded only once per page
|
|
3
|
+
|
|
4
|
+
interface GoogleMapsLoaderOptions {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
libraries?: string[];
|
|
7
|
+
version?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class GoogleMapsLoader {
|
|
11
|
+
private static instance: GoogleMapsLoader;
|
|
12
|
+
private loadPromise: Promise<void> | null = null;
|
|
13
|
+
private isLoaded = false;
|
|
14
|
+
private isLoading = false;
|
|
15
|
+
|
|
16
|
+
private constructor() {}
|
|
17
|
+
|
|
18
|
+
public static getInstance(): GoogleMapsLoader {
|
|
19
|
+
if (!GoogleMapsLoader.instance) {
|
|
20
|
+
GoogleMapsLoader.instance = new GoogleMapsLoader();
|
|
21
|
+
}
|
|
22
|
+
return GoogleMapsLoader.instance;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public async loadGoogleMaps(options: GoogleMapsLoaderOptions): Promise<void> {
|
|
26
|
+
// If already loaded, resolve immediately
|
|
27
|
+
if (this.isLoaded) {
|
|
28
|
+
return Promise.resolve();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// If currently loading, return the existing promise
|
|
32
|
+
if (this.isLoading && this.loadPromise) {
|
|
33
|
+
return this.loadPromise;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check if Google Maps is already available (loaded externally)
|
|
37
|
+
if (typeof window !== 'undefined' && window.google?.maps) {
|
|
38
|
+
this.isLoaded = true;
|
|
39
|
+
return Promise.resolve();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Start loading
|
|
43
|
+
this.isLoading = true;
|
|
44
|
+
this.loadPromise = this.createLoadPromise(options);
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
await this.loadPromise;
|
|
48
|
+
this.isLoaded = true;
|
|
49
|
+
this.isLoading = false;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
this.isLoading = false;
|
|
52
|
+
this.loadPromise = null;
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private createLoadPromise(options: GoogleMapsLoaderOptions): Promise<void> {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
// Check if script already exists
|
|
60
|
+
const existingScript = document.querySelector('script[src*="maps.googleapis.com"]');
|
|
61
|
+
if (existingScript) {
|
|
62
|
+
// Script exists, wait for it to load
|
|
63
|
+
if (window.google?.maps) {
|
|
64
|
+
resolve();
|
|
65
|
+
} else {
|
|
66
|
+
existingScript.addEventListener('load', () => resolve());
|
|
67
|
+
existingScript.addEventListener('error', (error) => reject(error));
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Create new script element
|
|
73
|
+
const script = document.createElement('script');
|
|
74
|
+
script.type = 'text/javascript';
|
|
75
|
+
script.async = true;
|
|
76
|
+
script.defer = true;
|
|
77
|
+
|
|
78
|
+
// Build URL with parameters
|
|
79
|
+
const { apiKey, libraries = ['geometry'], version = 'weekly' } = options;
|
|
80
|
+
const params = new URLSearchParams({
|
|
81
|
+
key: apiKey,
|
|
82
|
+
libraries: libraries.join(','),
|
|
83
|
+
v: version,
|
|
84
|
+
callback: '__googleMapsCallback__'
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
script.src = `https://maps.googleapis.com/maps/api/js?${params.toString()}`;
|
|
88
|
+
|
|
89
|
+
// Set up callback
|
|
90
|
+
(window as any).__googleMapsCallback__ = () => {
|
|
91
|
+
delete (window as any).__googleMapsCallback__;
|
|
92
|
+
resolve();
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Handle errors
|
|
96
|
+
script.onerror = (error) => {
|
|
97
|
+
delete (window as any).__googleMapsCallback__;
|
|
98
|
+
reject(new Error(`Failed to load Google Maps API: ${error}`));
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Add script to document
|
|
102
|
+
document.head.appendChild(script);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public isGoogleMapsLoaded(): boolean {
|
|
107
|
+
return this.isLoaded || (typeof window !== 'undefined' && !!window.google?.maps);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public getGoogleMaps(): typeof google.maps | null {
|
|
111
|
+
if (typeof window !== 'undefined' && window.google?.maps) {
|
|
112
|
+
return window.google.maps;
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Export singleton instance and convenience functions
|
|
119
|
+
export const googleMapsLoader = GoogleMapsLoader.getInstance();
|
|
120
|
+
|
|
121
|
+
export const loadGoogleMaps = (options: GoogleMapsLoaderOptions): Promise<void> => {
|
|
122
|
+
return googleMapsLoader.loadGoogleMaps(options);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const isGoogleMapsLoaded = (): boolean => {
|
|
126
|
+
return googleMapsLoader.isGoogleMapsLoaded();
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export const getGoogleMaps = (): typeof google.maps | null => {
|
|
130
|
+
return googleMapsLoader.getGoogleMaps();
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Type declarations for Google Maps
|
|
134
|
+
declare global {
|
|
135
|
+
interface Window {
|
|
136
|
+
google?: {
|
|
137
|
+
maps: typeof google.maps;
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { INodeTypeDescription, INodeProperties } from '../types/INodeProperties';
|
|
2
|
+
|
|
3
|
+
// Location parameter utilities
|
|
4
|
+
export interface LocationParams {
|
|
5
|
+
latParam: INodeProperties | null;
|
|
6
|
+
lngParam: INodeProperties | null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Find coordinate parameters in node definition
|
|
10
|
+
export const getCoordinateParams = (nodeDefinition: INodeTypeDescription | null): LocationParams => {
|
|
11
|
+
if (!nodeDefinition) return { latParam: null, lngParam: null };
|
|
12
|
+
|
|
13
|
+
const latParam = nodeDefinition.properties?.find((p: any) => {
|
|
14
|
+
const name = p.name.toLowerCase();
|
|
15
|
+
return name.includes('lat') || name === 'latitude';
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const lngParam = nodeDefinition.properties?.find((p: any) => {
|
|
19
|
+
const name = p.name.toLowerCase();
|
|
20
|
+
return name.includes('lng') || name.includes('lon') || name === 'longitude';
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return { latParam: latParam || null, lngParam: lngParam || null };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Get current coordinates from parameters
|
|
27
|
+
export const getCurrentCoordinates = (
|
|
28
|
+
parameters: Record<string, any>,
|
|
29
|
+
locationParams: LocationParams
|
|
30
|
+
) => {
|
|
31
|
+
const { latParam, lngParam } = locationParams;
|
|
32
|
+
|
|
33
|
+
const lat = (latParam && parameters[latParam.name]) || latParam?.default || 40.7128;
|
|
34
|
+
const lng = (lngParam && parameters[lngParam.name]) || lngParam?.default || -74.0060;
|
|
35
|
+
|
|
36
|
+
return { lat, lng };
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Get map display parameters
|
|
40
|
+
export const getMapDisplayParams = (parameters: Record<string, any>) => {
|
|
41
|
+
const zoom = parameters.zoom || 13;
|
|
42
|
+
const mapTypeId = parameters.mapTypeId || 'ROADMAP';
|
|
43
|
+
|
|
44
|
+
return { zoom, mapTypeId };
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Get Google Maps API key with fallbacks
|
|
48
|
+
export const getGoogleMapsApiKey = (): string | undefined => {
|
|
49
|
+
// Note: For stored API keys, use ApiKeyManagerService.getStoredApiKeyAsync('google_maps')
|
|
50
|
+
// This function now only returns the environment variable fallback
|
|
51
|
+
return (import.meta as any).env?.VITE_GOOGLE_MAPS_API_KEY || undefined;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Create location update handler
|
|
55
|
+
export const createLocationUpdateHandler = (
|
|
56
|
+
locationParams: LocationParams,
|
|
57
|
+
onParameterChange: (paramName: string, value: any) => void
|
|
58
|
+
) => {
|
|
59
|
+
return (lat: number, lng: number) => {
|
|
60
|
+
const { latParam, lngParam } = locationParams;
|
|
61
|
+
|
|
62
|
+
if (latParam) {
|
|
63
|
+
onParameterChange(latParam.name, lat);
|
|
64
|
+
}
|
|
65
|
+
if (lngParam) {
|
|
66
|
+
onParameterChange(lngParam.name, lng);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Check if a node is location-related
|
|
72
|
+
export const isLocationNode = (nodeDefinition: INodeTypeDescription | null): boolean => {
|
|
73
|
+
if (!nodeDefinition) return false;
|
|
74
|
+
|
|
75
|
+
const isCreateMap = nodeDefinition.name === 'createMap';
|
|
76
|
+
const hasLocationGroup = nodeDefinition.group?.includes('location') ?? false;
|
|
77
|
+
const hasCoordinates = getCoordinateParams(nodeDefinition).latParam !== null;
|
|
78
|
+
|
|
79
|
+
return isCreateMap || hasLocationGroup || hasCoordinates;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Check if node should show maps preview
|
|
83
|
+
export const shouldShowMapsPreview = (nodeDefinition: INodeTypeDescription | null): boolean => {
|
|
84
|
+
return nodeDefinition?.name === 'createMap';
|
|
85
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Utility functions for node operations
|
|
2
|
+
|
|
3
|
+
// Generate short node name from nodeType for parameter references
|
|
4
|
+
export const getShortNodeName = (nodeType: string): string => {
|
|
5
|
+
const typeMap: { [key: string]: string } = {
|
|
6
|
+
// AI Chat Models
|
|
7
|
+
'openaiChatModel': 'openai',
|
|
8
|
+
'anthropicChatModel': 'claude',
|
|
9
|
+
'geminiChatModel': 'gemini',
|
|
10
|
+
// AI Agents
|
|
11
|
+
'aiAgent': 'ai',
|
|
12
|
+
// Location Services
|
|
13
|
+
'createMap': 'map',
|
|
14
|
+
'addLocations': 'location',
|
|
15
|
+
'showNearbyPlaces': 'places'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// If we have a specific mapping, use it
|
|
19
|
+
if (typeMap[nodeType]) {
|
|
20
|
+
return typeMap[nodeType];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Otherwise, create a simplified version by removing capitals and common suffixes
|
|
24
|
+
let simplified = nodeType
|
|
25
|
+
.replace(/[A-Z]/g, '') // Remove capital letters
|
|
26
|
+
.replace(/(Get|Set|Stop|Play|Record|Control)$/i, '') // Remove common suffixes
|
|
27
|
+
.toLowerCase();
|
|
28
|
+
|
|
29
|
+
// Return simplified name or fallback to 'node'
|
|
30
|
+
return simplified || 'node';
|
|
31
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Node, Edge } from 'reactflow';
|
|
2
|
+
|
|
3
|
+
export const generateWorkflowId = (): string =>
|
|
4
|
+
`workflow-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
5
|
+
|
|
6
|
+
export const sanitizeNodesForComparison = (nodes: Node[]): Node[] =>
|
|
7
|
+
nodes.map(n => ({ ...n, selected: undefined, dragging: undefined }));
|
|
8
|
+
|
|
9
|
+
export const sanitizeEdgesForComparison = (edges: Edge[]): Edge[] =>
|
|
10
|
+
edges.map(e => ({ ...e, selected: undefined }));
|
|
11
|
+
|
|
12
|
+
export const serializeDateFields = <T extends { createdAt: Date; lastModified: Date }>(obj: T) => ({
|
|
13
|
+
...obj,
|
|
14
|
+
createdAt: obj.createdAt.toISOString(),
|
|
15
|
+
lastModified: obj.lastModified.toISOString(),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const deserializeDateFields = <T extends { createdAt: string; lastModified: string }>(obj: T) => ({
|
|
19
|
+
...obj,
|
|
20
|
+
createdAt: new Date(obj.createdAt),
|
|
21
|
+
lastModified: new Date(obj.lastModified),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const snapToGrid = (position: { x: number; y: number }, gridSize = 20) => ({
|
|
25
|
+
x: Math.round(position.x / gridSize) * gridSize,
|
|
26
|
+
y: Math.round(position.y / gridSize) * gridSize,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const getDefaultNodePosition = (nodeCount: number): { x: number; y: number } =>
|
|
30
|
+
nodeCount === 0 ? { x: 100, y: 200 } : { x: 0, y: 0 };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Export/Import Utilities
|
|
3
|
+
* Handles exporting workflows to JSON files and importing from JSON
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { WorkflowData } from '../store/useAppStore';
|
|
7
|
+
import { validateWorkflow, serializeWorkflow, deserializeWorkflow } from '../schemas/workflowSchema';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Export workflow to JSON file
|
|
11
|
+
*/
|
|
12
|
+
export function exportWorkflowToFile(workflow: WorkflowData): void {
|
|
13
|
+
const validation = validateWorkflow(workflow);
|
|
14
|
+
|
|
15
|
+
if (!validation.valid) {
|
|
16
|
+
console.error('Workflow validation errors:', validation.errors);
|
|
17
|
+
throw new Error(`Cannot export invalid workflow: ${validation.errors.join(', ')}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const workflowJSON = {
|
|
21
|
+
...workflow,
|
|
22
|
+
createdAt: workflow.createdAt.toISOString(),
|
|
23
|
+
lastModified: workflow.lastModified.toISOString(),
|
|
24
|
+
version: '1.0.0'
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const jsonString = serializeWorkflow(workflowJSON);
|
|
28
|
+
const blob = new Blob([jsonString], { type: 'application/json' });
|
|
29
|
+
const url = URL.createObjectURL(blob);
|
|
30
|
+
|
|
31
|
+
const a = document.createElement('a');
|
|
32
|
+
a.href = url;
|
|
33
|
+
a.download = `${workflow.name || 'workflow'}_${workflow.id}.json`;
|
|
34
|
+
document.body.appendChild(a);
|
|
35
|
+
a.click();
|
|
36
|
+
document.body.removeChild(a);
|
|
37
|
+
URL.revokeObjectURL(url);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Import workflow from JSON file
|
|
42
|
+
*/
|
|
43
|
+
export function importWorkflowFromFile(file: File): Promise<WorkflowData> {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
const reader = new FileReader();
|
|
46
|
+
|
|
47
|
+
reader.onload = (event) => {
|
|
48
|
+
try {
|
|
49
|
+
const jsonString = event.target?.result as string;
|
|
50
|
+
const workflow = deserializeWorkflow(jsonString);
|
|
51
|
+
|
|
52
|
+
const validation = validateWorkflow(workflow);
|
|
53
|
+
if (!validation.valid) {
|
|
54
|
+
reject(new Error(`Invalid workflow JSON: ${validation.errors.join(', ')}`));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
resolve(workflow);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
reject(error);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
reader.onerror = () => {
|
|
65
|
+
reject(new Error('Failed to read file'));
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
reader.readAsText(file);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Export workflow to JSON string
|
|
74
|
+
*/
|
|
75
|
+
export function exportWorkflowToJSON(workflow: WorkflowData): string {
|
|
76
|
+
const validation = validateWorkflow(workflow);
|
|
77
|
+
|
|
78
|
+
if (!validation.valid) {
|
|
79
|
+
throw new Error(`Cannot export invalid workflow: ${validation.errors.join(', ')}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const workflowJSON = {
|
|
83
|
+
...workflow,
|
|
84
|
+
createdAt: workflow.createdAt.toISOString(),
|
|
85
|
+
lastModified: workflow.lastModified.toISOString(),
|
|
86
|
+
version: '1.0.0'
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return serializeWorkflow(workflowJSON);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Import workflow from JSON string
|
|
94
|
+
*/
|
|
95
|
+
export function importWorkflowFromJSON(jsonString: string): WorkflowData {
|
|
96
|
+
const workflow = deserializeWorkflow(jsonString);
|
|
97
|
+
|
|
98
|
+
const validation = validateWorkflow(workflow);
|
|
99
|
+
if (!validation.valid) {
|
|
100
|
+
throw new Error(`Invalid workflow JSON: ${validation.errors.join(', ')}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return workflow;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Copy workflow to clipboard as JSON
|
|
108
|
+
*/
|
|
109
|
+
export async function copyWorkflowToClipboard(workflow: WorkflowData): Promise<void> {
|
|
110
|
+
const jsonString = exportWorkflowToJSON(workflow);
|
|
111
|
+
await navigator.clipboard.writeText(jsonString);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Paste workflow from clipboard
|
|
116
|
+
*/
|
|
117
|
+
export async function pasteWorkflowFromClipboard(): Promise<WorkflowData> {
|
|
118
|
+
const jsonString = await navigator.clipboard.readText();
|
|
119
|
+
return importWorkflowFromJSON(jsonString);
|
|
120
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
|
|
3
|
+
interface ImportMetaEnv {
|
|
4
|
+
readonly VITE_ANDROID_RELAY_URL: string;
|
|
5
|
+
readonly VITE_AUTH_ENABLED: string;
|
|
6
|
+
readonly VITE_CLIENT_PORT: string;
|
|
7
|
+
readonly VITE_PYTHON_BACKEND_URL: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface ImportMeta {
|
|
11
|
+
readonly env: ImportMetaEnv;
|
|
12
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** @type {import('tailwindcss').Config} */
|
|
2
|
+
export default {
|
|
3
|
+
darkMode: ["class"],
|
|
4
|
+
content: [
|
|
5
|
+
"./index.html",
|
|
6
|
+
"./src/**/*.{js,ts,jsx,tsx}",
|
|
7
|
+
],
|
|
8
|
+
theme: {
|
|
9
|
+
container: {
|
|
10
|
+
center: true,
|
|
11
|
+
padding: "2rem",
|
|
12
|
+
screens: {
|
|
13
|
+
"2xl": "1400px",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
extend: {
|
|
17
|
+
colors: {
|
|
18
|
+
border: "hsl(var(--border))",
|
|
19
|
+
input: "hsl(var(--input))",
|
|
20
|
+
ring: "hsl(var(--ring))",
|
|
21
|
+
background: "hsl(var(--background))",
|
|
22
|
+
foreground: "hsl(var(--foreground))",
|
|
23
|
+
primary: {
|
|
24
|
+
DEFAULT: "hsl(var(--primary))",
|
|
25
|
+
foreground: "hsl(var(--primary-foreground))",
|
|
26
|
+
},
|
|
27
|
+
secondary: {
|
|
28
|
+
DEFAULT: "hsl(var(--secondary))",
|
|
29
|
+
foreground: "hsl(var(--secondary-foreground))",
|
|
30
|
+
},
|
|
31
|
+
destructive: {
|
|
32
|
+
DEFAULT: "hsl(var(--destructive))",
|
|
33
|
+
foreground: "hsl(var(--destructive-foreground))",
|
|
34
|
+
},
|
|
35
|
+
muted: {
|
|
36
|
+
DEFAULT: "hsl(var(--muted))",
|
|
37
|
+
foreground: "hsl(var(--muted-foreground))",
|
|
38
|
+
},
|
|
39
|
+
accent: {
|
|
40
|
+
DEFAULT: "hsl(var(--accent))",
|
|
41
|
+
foreground: "hsl(var(--accent-foreground))",
|
|
42
|
+
},
|
|
43
|
+
popover: {
|
|
44
|
+
DEFAULT: "hsl(var(--popover))",
|
|
45
|
+
foreground: "hsl(var(--popover-foreground))",
|
|
46
|
+
},
|
|
47
|
+
card: {
|
|
48
|
+
DEFAULT: "hsl(var(--card))",
|
|
49
|
+
foreground: "hsl(var(--card-foreground))",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
borderRadius: {
|
|
53
|
+
lg: "var(--radius)",
|
|
54
|
+
md: "calc(var(--radius) - 2px)",
|
|
55
|
+
sm: "calc(var(--radius) - 4px)",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
plugins: [],
|
|
60
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"jsx": "react-jsx",
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true
|
|
22
|
+
},
|
|
23
|
+
"include": ["src"],
|
|
24
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
25
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { defineConfig, loadEnv } from 'vite'
|
|
2
|
+
import react from '@vitejs/plugin-react'
|
|
3
|
+
import { resolve } from 'path'
|
|
4
|
+
|
|
5
|
+
// https://vite.dev/config/
|
|
6
|
+
export default defineConfig(({ mode }) => {
|
|
7
|
+
// Load env from parent directory (root .env) for local development
|
|
8
|
+
const fileEnv = loadEnv(mode, resolve(process.cwd(), '..'), '')
|
|
9
|
+
|
|
10
|
+
// In Docker, env vars are set via ENV in Dockerfile, accessible via process.env
|
|
11
|
+
// Priority: process.env (Docker) > fileEnv (local .env) > defaults
|
|
12
|
+
const getEnv = (key, defaultValue = '') => {
|
|
13
|
+
return process.env[key] || fileEnv[key] || defaultValue
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
plugins: [react()],
|
|
18
|
+
// Expose VITE_ prefixed env vars to client code via import.meta.env
|
|
19
|
+
define: {
|
|
20
|
+
'import.meta.env.VITE_AUTH_ENABLED': JSON.stringify(getEnv('VITE_AUTH_ENABLED', 'true')),
|
|
21
|
+
'import.meta.env.VITE_PYTHON_SERVICE_URL': JSON.stringify(getEnv('VITE_PYTHON_SERVICE_URL', '')),
|
|
22
|
+
'import.meta.env.VITE_WHATSAPP_SERVICE_URL': JSON.stringify(getEnv('VITE_WHATSAPP_SERVICE_URL', '')),
|
|
23
|
+
'import.meta.env.VITE_ANDROID_RELAY_URL': JSON.stringify(getEnv('VITE_ANDROID_RELAY_URL', '')),
|
|
24
|
+
},
|
|
25
|
+
server: {
|
|
26
|
+
port: parseInt(getEnv('VITE_CLIENT_PORT', '3000')),
|
|
27
|
+
strictPort: false,
|
|
28
|
+
host: true
|
|
29
|
+
},
|
|
30
|
+
build: {
|
|
31
|
+
// antd + reactflow are large libraries - this is expected
|
|
32
|
+
chunkSizeWarningLimit: 1500,
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
})
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
services:
|
|
2
|
+
# Redis Cache (production performance)
|
|
3
|
+
redis:
|
|
4
|
+
image: redis:7-alpine
|
|
5
|
+
container_name: machina-redis
|
|
6
|
+
volumes:
|
|
7
|
+
- redis-data:/data
|
|
8
|
+
networks:
|
|
9
|
+
- app-network
|
|
10
|
+
restart: always
|
|
11
|
+
healthcheck:
|
|
12
|
+
test: ["CMD", "redis-cli", "ping"]
|
|
13
|
+
interval: 10s
|
|
14
|
+
timeout: 5s
|
|
15
|
+
retries: 3
|
|
16
|
+
command: redis-server --appendonly yes
|
|
17
|
+
|
|
18
|
+
# Python FastAPI Backend
|
|
19
|
+
backend:
|
|
20
|
+
build:
|
|
21
|
+
context: ./server
|
|
22
|
+
dockerfile: Dockerfile
|
|
23
|
+
container_name: machina-backend
|
|
24
|
+
ports:
|
|
25
|
+
- "${PYTHON_BACKEND_PORT:-3010}:${PYTHON_BACKEND_PORT:-3010}"
|
|
26
|
+
volumes:
|
|
27
|
+
- backend-data:/app/data
|
|
28
|
+
networks:
|
|
29
|
+
- app-network
|
|
30
|
+
restart: always
|
|
31
|
+
healthcheck:
|
|
32
|
+
test: ["CMD", "curl", "-f", "http://localhost:${PYTHON_BACKEND_PORT:-3010}/health"]
|
|
33
|
+
interval: 30s
|
|
34
|
+
timeout: 10s
|
|
35
|
+
retries: 3
|
|
36
|
+
start_period: 30s
|
|
37
|
+
env_file:
|
|
38
|
+
- path: ./.env
|
|
39
|
+
required: false
|
|
40
|
+
environment:
|
|
41
|
+
# Production overrides
|
|
42
|
+
- PORT=${PYTHON_BACKEND_PORT:-3010}
|
|
43
|
+
- ENV=production
|
|
44
|
+
- REDIS_ENABLED=true
|
|
45
|
+
- REDIS_URL=redis://redis:6379
|
|
46
|
+
- WHATSAPP_RPC_URL=ws://whatsapp:${WHATSAPP_RPC_PORT:-9400}/ws/rpc
|
|
47
|
+
- TZ=UTC
|
|
48
|
+
depends_on:
|
|
49
|
+
redis:
|
|
50
|
+
condition: service_healthy
|
|
51
|
+
whatsapp:
|
|
52
|
+
condition: service_healthy
|
|
53
|
+
|
|
54
|
+
# React Frontend (Production - Nginx)
|
|
55
|
+
frontend:
|
|
56
|
+
build:
|
|
57
|
+
context: ./client
|
|
58
|
+
dockerfile: Dockerfile
|
|
59
|
+
target: production
|
|
60
|
+
args:
|
|
61
|
+
# Pass env vars to frontend build (baked into static JS at build time)
|
|
62
|
+
VITE_AUTH_ENABLED: ${VITE_AUTH_ENABLED:-true}
|
|
63
|
+
VITE_PYTHON_SERVICE_URL: ${VITE_PYTHON_SERVICE_URL:-}
|
|
64
|
+
VITE_WHATSAPP_SERVICE_URL: ${VITE_WHATSAPP_SERVICE_URL:-}
|
|
65
|
+
VITE_ANDROID_RELAY_URL: ${VITE_ANDROID_RELAY_URL:-}
|
|
66
|
+
container_name: machina-frontend
|
|
67
|
+
ports:
|
|
68
|
+
- "${VITE_CLIENT_PORT:-3000}:80"
|
|
69
|
+
networks:
|
|
70
|
+
- app-network
|
|
71
|
+
restart: always
|
|
72
|
+
depends_on:
|
|
73
|
+
- backend
|
|
74
|
+
|
|
75
|
+
# WhatsApp RPC Service (Go API only)
|
|
76
|
+
whatsapp:
|
|
77
|
+
build:
|
|
78
|
+
context: ./server/whatsapp-rpc
|
|
79
|
+
dockerfile: Dockerfile
|
|
80
|
+
container_name: machina-whatsapp
|
|
81
|
+
volumes:
|
|
82
|
+
- whatsapp-data:/app/data
|
|
83
|
+
networks:
|
|
84
|
+
- app-network
|
|
85
|
+
restart: always
|
|
86
|
+
# Privileged mode for proper SQLite WAL mmap support
|
|
87
|
+
privileged: true
|
|
88
|
+
healthcheck:
|
|
89
|
+
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9400/health"]
|
|
90
|
+
interval: 30s
|
|
91
|
+
timeout: 10s
|
|
92
|
+
retries: 3
|
|
93
|
+
start_period: 15s
|
|
94
|
+
environment:
|
|
95
|
+
- TZ=UTC
|
|
96
|
+
|
|
97
|
+
networks:
|
|
98
|
+
app-network:
|
|
99
|
+
driver: bridge
|
|
100
|
+
|
|
101
|
+
volumes:
|
|
102
|
+
redis-data:
|
|
103
|
+
driver: local
|
|
104
|
+
backend-data:
|
|
105
|
+
driver: local
|
|
106
|
+
whatsapp-data:
|
|
107
|
+
driver: local
|