@paimaexample/npm-midnight-indexer 0.3.15
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/README.md +211 -0
- package/binary.js +79 -0
- package/docker.js +164 -0
- package/index.js +305 -0
- package/package.json +24 -0
- package/run_midnight_indexer.js +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Midnight Indexer
|
|
2
|
+
|
|
3
|
+
A Node.js package that downloads and runs the [Midnight](https://midnight.network) Indexer. The indexer requires a running Midnight Node to function properly.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package provides flexible execution options for the Midnight Indexer:
|
|
8
|
+
|
|
9
|
+
- **Docker mode**: Runs the indexer in a Docker container (recommended)
|
|
10
|
+
- **Binary mode**: Downloads and runs the native binary locally (Linux only)
|
|
11
|
+
|
|
12
|
+
### Platform Support
|
|
13
|
+
|
|
14
|
+
| Platform | Docker | Binary |
|
|
15
|
+
|----------|--------|--------|
|
|
16
|
+
| macOS | ✅ Yes | ❌ No |
|
|
17
|
+
| Linux | ✅ Yes | ✅ Yes |
|
|
18
|
+
| Windows | ❌ No | ❌ No |
|
|
19
|
+
|
|
20
|
+
> **Note**: On macOS, only Docker execution is supported. Binary execution will automatically fall back to Docker or show an error.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install npm-midnight-indexer
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Prerequisites
|
|
29
|
+
|
|
30
|
+
### For Docker Mode
|
|
31
|
+
- Docker installed and running
|
|
32
|
+
- `APP__INFRA__SECRET` environment variable (required)
|
|
33
|
+
|
|
34
|
+
### For Binary Mode
|
|
35
|
+
- Supported platform (Linux/Windows)
|
|
36
|
+
- Local Midnight Node and Proof Server running on standard ports
|
|
37
|
+
- `APP__INFRA__SECRET` environment variable (required)
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
### Command Line Options
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
node index.js [options] [args...]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Options:**
|
|
48
|
+
- `--docker` - Force Docker execution
|
|
49
|
+
- `--binary` - Force binary execution (not available on macOS)
|
|
50
|
+
- `--help` - Show help information
|
|
51
|
+
|
|
52
|
+
**Examples:**
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Force Docker usage
|
|
56
|
+
APP__INFRA__SECRET=mysecret node index.js --docker
|
|
57
|
+
|
|
58
|
+
# Force binary usage (Linux/Windows only)
|
|
59
|
+
APP__INFRA__SECRET=mysecret node index.js --binary
|
|
60
|
+
|
|
61
|
+
# Interactive mode - prompts for Docker vs binary choice
|
|
62
|
+
APP__INFRA__SECRET=mysecret node index.js
|
|
63
|
+
|
|
64
|
+
# Show help
|
|
65
|
+
node index.js --help
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Environment Variables
|
|
69
|
+
|
|
70
|
+
The indexer supports several environment variables with different defaults based on execution mode:
|
|
71
|
+
|
|
72
|
+
| Variable | Required | Docker Default | Binary Default | Description |
|
|
73
|
+
|----------|----------|----------------|----------------|-------------|
|
|
74
|
+
| `APP__INFRA__SECRET` | Yes | - | - | Secret key for the application |
|
|
75
|
+
| `LEDGER_NETWORK_ID` | No | `Undeployed` | `Undeployed` | Ledger network identifier |
|
|
76
|
+
| `SUBSTRATE_NODE_WS_URL` | No | `ws://node:9944` | `ws://localhost:9944` | Substrate node WebSocket URL |
|
|
77
|
+
| `FEATURES_WALLET_ENABLED` | No | `true` | `true` | Enable wallet features |
|
|
78
|
+
| `APP__INFRA__PROOF_SERVER__URL` | No | `http://proof-server:6300` | `http://localhost:6300` | Proof server URL |
|
|
79
|
+
| `APP__INFRA__NODE__URL` | No | `ws://node:9944` | `ws://localhost:9944` | Node WebSocket URL |
|
|
80
|
+
|
|
81
|
+
### Environment Variable Examples
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Minimal Docker setup
|
|
85
|
+
APP__INFRA__SECRET=A5F07FCAC914A181EC0998CCCA68312DA5F07FCAC914A181EC0998CCCA68312D \
|
|
86
|
+
node index.js --docker
|
|
87
|
+
|
|
88
|
+
# Custom configuration
|
|
89
|
+
APP__INFRA__SECRET=mysecret \
|
|
90
|
+
LEDGER_NETWORK_ID=CustomNetwork \
|
|
91
|
+
SUBSTRATE_NODE_WS_URL=ws://localhost:9944 \
|
|
92
|
+
APP__INFRA__PROOF_SERVER__URL=http://localhost:6300 \
|
|
93
|
+
node index.js --docker
|
|
94
|
+
|
|
95
|
+
# Binary mode (uses localhost defaults automatically)
|
|
96
|
+
APP__INFRA__SECRET=mysecret node index.js --binary
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Docker Mode Details
|
|
100
|
+
|
|
101
|
+
When using Docker mode, the indexer:
|
|
102
|
+
|
|
103
|
+
1. **Pulls the latest image**: `midnightntwrk/indexer-standalone`
|
|
104
|
+
2. **Maps port 8088**: Container port 8088 → Host port 8088
|
|
105
|
+
3. **Uses container networking**: Defaults work with Docker Compose setups
|
|
106
|
+
4. **Auto-cleanup**: Removes existing containers before starting new ones
|
|
107
|
+
|
|
108
|
+
### Docker Command Generated
|
|
109
|
+
|
|
110
|
+
The package generates Docker commands similar to:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
docker run --rm --name midnight-local-indexer \
|
|
114
|
+
-p 8088:8088 \
|
|
115
|
+
-e LEDGER_NETWORK_ID=Undeployed \
|
|
116
|
+
-e SUBSTRATE_NODE_WS_URL=ws://node:9944 \
|
|
117
|
+
-e APP__INFRA__SECRET=A5F07FCAC914A181EC0998CCCA68312DA5F07FCAC914A181EC0998CCCA68312D \
|
|
118
|
+
-e FEATURES_WALLET_ENABLED=true \
|
|
119
|
+
-e APP__INFRA__PROOF_SERVER__URL=http://proof-server:6300 \
|
|
120
|
+
-e APP__INFRA__NODE__URL=ws://node:9944 \
|
|
121
|
+
midnightntwrk/indexer-standalone
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Binary Mode Details
|
|
125
|
+
|
|
126
|
+
When using binary mode, the indexer:
|
|
127
|
+
|
|
128
|
+
1. **Downloads platform-specific binary** if not already present
|
|
129
|
+
2. **Uses localhost URLs** for all service connections
|
|
130
|
+
3. **Runs natively** on the host system
|
|
131
|
+
|
|
132
|
+
### Supported Binary Platforms
|
|
133
|
+
|
|
134
|
+
- Linux ARM64 (`linux-arm64`)
|
|
135
|
+
- Linux AMD64 (`linux-amd64`)
|
|
136
|
+
- macOS ARM64 (`macos-arm64`) - **Docker only**
|
|
137
|
+
|
|
138
|
+
## Troubleshooting
|
|
139
|
+
|
|
140
|
+
### Common Issues
|
|
141
|
+
|
|
142
|
+
**"Docker is not installed or not available"**
|
|
143
|
+
- Install Docker Desktop or Docker Engine
|
|
144
|
+
- Ensure Docker is running
|
|
145
|
+
- Check Docker is accessible from command line: `docker --version`
|
|
146
|
+
|
|
147
|
+
**"APP__INFRA__SECRET environment variable is required"**
|
|
148
|
+
- Set the secret: `export APP__INFRA__SECRET=your_secret_here`
|
|
149
|
+
- Or pass inline: `APP__INFRA__SECRET=your_secret node index.js --docker`
|
|
150
|
+
- Required for both Docker and binary execution modes
|
|
151
|
+
|
|
152
|
+
**"Binary execution is not supported on macOS"**
|
|
153
|
+
- Use `--docker` flag or run without flags to use Docker automatically
|
|
154
|
+
- Install Docker if not already installed
|
|
155
|
+
|
|
156
|
+
**"Failed to start midnight-indexer"**
|
|
157
|
+
- Check if ports 8088, 6300, 9944 are available
|
|
158
|
+
- Verify Midnight Node is running and accessible
|
|
159
|
+
- Check environment variables are correctly set
|
|
160
|
+
|
|
161
|
+
### Logs and Debugging
|
|
162
|
+
|
|
163
|
+
The indexer outputs detailed logs including:
|
|
164
|
+
- Download progress for binaries
|
|
165
|
+
- Docker pull progress
|
|
166
|
+
- Process IDs and status
|
|
167
|
+
- Error messages with suggestions
|
|
168
|
+
|
|
169
|
+
## Development
|
|
170
|
+
|
|
171
|
+
### Package Structure
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
npm-midnight-indexer/
|
|
175
|
+
├── index.js # Main entry point
|
|
176
|
+
├── binary.js # Binary download logic
|
|
177
|
+
├── docker.js # Docker execution logic
|
|
178
|
+
├── run_midnight_indexer.js # Binary execution logic
|
|
179
|
+
├── package.json # Package configuration
|
|
180
|
+
└── README.md # This file
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Testing
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Test Docker detection
|
|
187
|
+
node -e "const { checkIfDockerExists } = require('./docker.js'); checkIfDockerExists().then(console.log)"
|
|
188
|
+
|
|
189
|
+
# Test help
|
|
190
|
+
node index.js --help
|
|
191
|
+
|
|
192
|
+
# Test platform detection
|
|
193
|
+
node -e "console.log('Platform:', require('os').platform())"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
ISC
|
|
199
|
+
|
|
200
|
+
## Support
|
|
201
|
+
|
|
202
|
+
For issues related to:
|
|
203
|
+
- **This package**: Open an issue in the repository
|
|
204
|
+
- **Midnight Protocol**: Visit [midnight.network](https://midnight.network)
|
|
205
|
+
- **Docker**: Check [Docker documentation](https://docs.docker.com/)
|
|
206
|
+
|
|
207
|
+
## Related Links
|
|
208
|
+
|
|
209
|
+
- [Midnight Network](https://midnight.network)
|
|
210
|
+
- [Docker Hub - Midnight Indexer](https://hub.docker.com/r/midnightntwrk/indexer-standalone)
|
|
211
|
+
- [Node.js](https://nodejs.org/)
|
package/binary.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const os = require("os");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const axios = require("axios");
|
|
4
|
+
const extract = require("extract-zip");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
|
|
7
|
+
function getPlatform() {
|
|
8
|
+
let platform = os.platform();
|
|
9
|
+
const arch = os.arch();
|
|
10
|
+
|
|
11
|
+
if (platform === "darwin") {
|
|
12
|
+
// For macOS, return the full platform-arch combination
|
|
13
|
+
if (arch === "arm64") {
|
|
14
|
+
return "macos-arm64";
|
|
15
|
+
} else if (arch === "x64") {
|
|
16
|
+
return "macos-amd64"; // Will not be in supportedPlatforms, so will fall back to Docker
|
|
17
|
+
} else {
|
|
18
|
+
return `macos-${arch}`;
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
// For Linux and other platforms, only arch is needed
|
|
22
|
+
if (arch === "x64") {
|
|
23
|
+
return "amd64";
|
|
24
|
+
}
|
|
25
|
+
return arch;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getBinaryUrl() {
|
|
30
|
+
const platform = getPlatform();
|
|
31
|
+
const supportedPlatforms = require("./package.json").supportedPlatforms;
|
|
32
|
+
|
|
33
|
+
// Check if platform is supported
|
|
34
|
+
if (!supportedPlatforms.includes(platform)) {
|
|
35
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Special handling for macOS platforms
|
|
39
|
+
if (platform.startsWith("macos")) {
|
|
40
|
+
return `https://paima-midnight.nyc3.cdn.digitaloceanspaces.com/binaries/indexer-standalone-${platform}.zip`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// For Linux and other platforms, use the existing pattern
|
|
44
|
+
return `https://paima-midnight.nyc3.cdn.digitaloceanspaces.com/binaries/indexer-standalone-${platform}.zip`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function downloadAndSaveBinary() {
|
|
48
|
+
const url = getBinaryUrl();
|
|
49
|
+
try {
|
|
50
|
+
const response = await axios.get(url, { responseType: "stream" });
|
|
51
|
+
const writer = fs.createWriteStream(path.join(__dirname, "indexer-standalone.zip"));
|
|
52
|
+
|
|
53
|
+
response.data.pipe(writer);
|
|
54
|
+
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
writer.on('finish', resolve);
|
|
57
|
+
writer.on('error', reject);
|
|
58
|
+
});
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error("Error downloading binary:", error);
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function unzipBinary() {
|
|
66
|
+
await extract(path.join(__dirname, "indexer-standalone.zip"), {
|
|
67
|
+
dir: path.join(__dirname, "indexer-standalone"),
|
|
68
|
+
});
|
|
69
|
+
fs.unlinkSync(path.join(__dirname, "indexer-standalone.zip"));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function binary() {
|
|
73
|
+
await downloadAndSaveBinary();
|
|
74
|
+
await unzipBinary();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = {
|
|
78
|
+
binary,
|
|
79
|
+
};
|
package/docker.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
const { spawn, exec } = require('child_process');
|
|
2
|
+
const { promisify } = require('util');
|
|
3
|
+
const execAsync = promisify(exec);
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Checks if Docker is installed and available on the system
|
|
7
|
+
* @returns {Promise<boolean>} True if Docker is available, false otherwise
|
|
8
|
+
*/
|
|
9
|
+
async function checkIfDockerExists() {
|
|
10
|
+
try {
|
|
11
|
+
await execAsync('docker --version');
|
|
12
|
+
return true;
|
|
13
|
+
} catch (error) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Pulls the latest midnight indexer Docker image
|
|
20
|
+
* @returns {Promise<void>}
|
|
21
|
+
*/
|
|
22
|
+
async function pullDockerImage() {
|
|
23
|
+
const imageName = 'midnightntwrk/indexer-standalone';
|
|
24
|
+
|
|
25
|
+
console.log(`Pulling Docker image: ${imageName}`);
|
|
26
|
+
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const childProcess = spawn('docker', ['pull', imageName], {
|
|
29
|
+
stdio: 'inherit'
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
childProcess.on('exit', (code) => {
|
|
33
|
+
if (code === 0) {
|
|
34
|
+
console.log('Docker image pulled successfully');
|
|
35
|
+
resolve();
|
|
36
|
+
} else {
|
|
37
|
+
reject(new Error(`Docker pull failed with exit code ${code}`));
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
childProcess.on('error', (error) => {
|
|
42
|
+
reject(new Error(`Failed to pull Docker image: ${error.message}`));
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Runs the midnight indexer Docker container
|
|
49
|
+
* @param {Object} env - Environment variables to pass to the container
|
|
50
|
+
* @param {Array} args - Arguments to pass to the container
|
|
51
|
+
* @returns {ChildProcess} The spawned Docker process
|
|
52
|
+
*/
|
|
53
|
+
function runDockerContainer(env = process.env, args = []) {
|
|
54
|
+
const imageName = 'midnightntwrk/indexer-standalone';
|
|
55
|
+
const containerName = 'midnight-local-indexer';
|
|
56
|
+
|
|
57
|
+
// Stop and remove existing container if it exists
|
|
58
|
+
try {
|
|
59
|
+
require('child_process').execSync(`docker stop ${containerName} 2>/dev/null || true`, { stdio: 'ignore' });
|
|
60
|
+
require('child_process').execSync(`docker rm ${containerName} 2>/dev/null || true`, { stdio: 'ignore' });
|
|
61
|
+
} catch (error) {
|
|
62
|
+
// Ignore errors - container might not exist
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Build Docker run command arguments
|
|
66
|
+
const dockerArgs = ['run'];
|
|
67
|
+
|
|
68
|
+
// Add container name (remove existing container if it exists)
|
|
69
|
+
dockerArgs.push('--rm'); // Automatically remove container when it exits
|
|
70
|
+
dockerArgs.push('--name', containerName);
|
|
71
|
+
|
|
72
|
+
// Intelligent port mapping - default to 8088:8088 if not specified
|
|
73
|
+
const defaultPort = '8088:8088';
|
|
74
|
+
let portMapping = defaultPort;
|
|
75
|
+
|
|
76
|
+
// Check if port is specified in args
|
|
77
|
+
const portArgIndex = args.findIndex(arg => arg.includes('--port') || arg.includes('-p'));
|
|
78
|
+
if (portArgIndex !== -1 && args[portArgIndex + 1]) {
|
|
79
|
+
const specifiedPort = args[portArgIndex + 1];
|
|
80
|
+
portMapping = `${specifiedPort}:${specifiedPort}`;
|
|
81
|
+
// Remove port args from the args array as they're handled by Docker
|
|
82
|
+
args.splice(portArgIndex, 2);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
dockerArgs.push('-p', portMapping);
|
|
86
|
+
|
|
87
|
+
// Add environment variables
|
|
88
|
+
const requiredEnvVars = [
|
|
89
|
+
'LEDGER_NETWORK_ID',
|
|
90
|
+
'SUBSTRATE_NODE_WS_URL',
|
|
91
|
+
'APP__INFRA__SECRET',
|
|
92
|
+
'FEATURES_WALLET_ENABLED',
|
|
93
|
+
'APP__INFRA__PROOF_SERVER__URL',
|
|
94
|
+
'APP__INFRA__NODE__URL'
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
// Set default values for Docker environment variables (using container network names)
|
|
98
|
+
const envDefaults = {
|
|
99
|
+
'LEDGER_NETWORK_ID': env.LEDGER_NETWORK_ID || 'Undeployed',
|
|
100
|
+
'SUBSTRATE_NODE_WS_URL': env.SUBSTRATE_NODE_WS_URL || 'ws://node:9944',
|
|
101
|
+
'APP__INFRA__SECRET': env.APP__INFRA__SECRET,
|
|
102
|
+
'FEATURES_WALLET_ENABLED': env.FEATURES_WALLET_ENABLED || 'true',
|
|
103
|
+
'APP__INFRA__PROOF_SERVER__URL': env.APP__INFRA__PROOF_SERVER__URL || 'http://proof-server:6300',
|
|
104
|
+
'APP__INFRA__NODE__URL': env.APP__INFRA__NODE__URL || 'ws://node:9944'
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Validate that SECRET is provided
|
|
108
|
+
if (!envDefaults['APP__INFRA__SECRET']) {
|
|
109
|
+
throw new Error('APP__INFRA__SECRET environment variable is required');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Add environment variables to Docker command
|
|
113
|
+
Object.entries(envDefaults).forEach(([key, value]) => {
|
|
114
|
+
if (value) {
|
|
115
|
+
dockerArgs.push('-e', `${key}=${value}`);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Add any additional environment variables from env object
|
|
120
|
+
Object.entries(env).forEach(([key, value]) => {
|
|
121
|
+
if (!requiredEnvVars.includes(key) && value) {
|
|
122
|
+
dockerArgs.push('-e', `${key}=${value}`);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Add the image name
|
|
127
|
+
dockerArgs.push(imageName);
|
|
128
|
+
|
|
129
|
+
// Add any remaining arguments
|
|
130
|
+
if (args.length > 0) {
|
|
131
|
+
dockerArgs.push(...args);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
console.log(`Starting Docker container: docker ${dockerArgs.join(' ')}`);
|
|
135
|
+
|
|
136
|
+
const childProcess = spawn('docker', dockerArgs, {
|
|
137
|
+
stdio: 'inherit'
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
childProcess.on('spawn', () => {
|
|
141
|
+
console.log(`Docker container started with PID: ${childProcess.pid}`);
|
|
142
|
+
console.log(`Container accessible on port: ${portMapping.split(':')[0]}`);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
childProcess.on('error', (error) => {
|
|
146
|
+
console.error('Failed to start Docker container:', error);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
childProcess.on('exit', (code, signal) => {
|
|
150
|
+
if (code !== null) {
|
|
151
|
+
console.log(`Docker container exited with code: ${code}`);
|
|
152
|
+
} else {
|
|
153
|
+
console.log(`Docker container terminated by signal: ${signal}`);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
return childProcess;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = {
|
|
161
|
+
checkIfDockerExists,
|
|
162
|
+
pullDockerImage,
|
|
163
|
+
runDockerContainer
|
|
164
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
const { binary } = require("./binary");
|
|
2
|
+
const { runMidnightIndexer } = require("./run_midnight_indexer");
|
|
3
|
+
const { checkIfDockerExists, pullDockerImage, runDockerContainer } = require(
|
|
4
|
+
"./docker",
|
|
5
|
+
);
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const readline = require("readline");
|
|
9
|
+
const os = require("os");
|
|
10
|
+
|
|
11
|
+
function checkIfBinaryExists() {
|
|
12
|
+
return fs.existsSync(
|
|
13
|
+
path.join(__dirname, "indexer-standalone", "indexer-standalone"),
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Checks if the current platform supports binary execution
|
|
19
|
+
* @returns {boolean} True if binary execution is supported
|
|
20
|
+
*/
|
|
21
|
+
function isBinarySupported() {
|
|
22
|
+
const platform = os.platform();
|
|
23
|
+
const arch = os.arch();
|
|
24
|
+
|
|
25
|
+
// Check if the current platform-arch combination is supported
|
|
26
|
+
const supportedPlatforms = require("./package.json").supportedPlatforms;
|
|
27
|
+
|
|
28
|
+
let platformString;
|
|
29
|
+
if (platform === "darwin") {
|
|
30
|
+
platformString = arch === "x64" ? "macos-amd64" : `macos-${arch}`;
|
|
31
|
+
} else {
|
|
32
|
+
platformString = arch === "x64" ? "amd64" : arch;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return supportedPlatforms.includes(platformString);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Prompts the user to choose between Docker and binary execution
|
|
40
|
+
* @returns {Promise<boolean>} True if user chooses Docker, false for binary
|
|
41
|
+
*/
|
|
42
|
+
function promptUserForDockerChoice() {
|
|
43
|
+
const rl = readline.createInterface({
|
|
44
|
+
input: process.stdin,
|
|
45
|
+
output: process.stdout,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
rl.question(
|
|
50
|
+
"Docker is available. Would you like to use Docker instead of downloading the binary? (y/n): ",
|
|
51
|
+
(answer) => {
|
|
52
|
+
rl.close();
|
|
53
|
+
resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Shows usage information
|
|
61
|
+
*/
|
|
62
|
+
function showUsage() {
|
|
63
|
+
console.log(`
|
|
64
|
+
Usage: node index.js [options] [args...]
|
|
65
|
+
|
|
66
|
+
Options:
|
|
67
|
+
--docker Force use of Docker container
|
|
68
|
+
--binary Force use of binary execution (supports Linux and macOS arm64)
|
|
69
|
+
--help Show this help message
|
|
70
|
+
|
|
71
|
+
Environment Variables:
|
|
72
|
+
APP__INFRA__SECRET Required: Secret key for the application
|
|
73
|
+
LEDGER_NETWORK_ID Optional: Ledger network ID (default: Undeployed)
|
|
74
|
+
SUBSTRATE_NODE_WS_URL Optional: Substrate node WebSocket URL
|
|
75
|
+
(Docker default: ws://node:9944, Binary default: ws://localhost:9944)
|
|
76
|
+
FEATURES_WALLET_ENABLED Optional: Enable wallet features (default: true)
|
|
77
|
+
APP__INFRA__PROOF_SERVER__URL Optional: Proof server URL
|
|
78
|
+
(Docker default: http://proof-server:6300, Binary default: http://localhost:6300)
|
|
79
|
+
APP__INFRA__NODE__URL Optional: Node URL
|
|
80
|
+
(Docker default: ws://node:9944, Binary default: ws://localhost:9944)
|
|
81
|
+
|
|
82
|
+
Note: macOS Intel users will automatically use Docker. macOS arm64 supports both binary and Docker execution.
|
|
83
|
+
|
|
84
|
+
Examples:
|
|
85
|
+
APP__INFRA__SECRET=mysecret node index.js --docker # Use Docker
|
|
86
|
+
APP__INFRA__SECRET=mysecret node index.js --binary # Use binary (if supported)
|
|
87
|
+
APP__INFRA__SECRET=mysecret node index.js # Interactive mode (or auto-Docker on unsupported platforms)
|
|
88
|
+
node index.js --help # Show this help
|
|
89
|
+
`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Parses command line arguments to extract flags and remaining args
|
|
94
|
+
* @param {Array} args - Command line arguments
|
|
95
|
+
* @returns {Object} Object containing useDocker, useBinary flags and remaining args
|
|
96
|
+
*/
|
|
97
|
+
function parseFlags(args) {
|
|
98
|
+
const flags = {
|
|
99
|
+
useDocker: false,
|
|
100
|
+
useBinary: false,
|
|
101
|
+
showHelp: false,
|
|
102
|
+
remainingArgs: [],
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
for (let i = 0; i < args.length; i++) {
|
|
106
|
+
if (args[i] === "--docker") {
|
|
107
|
+
flags.useDocker = true;
|
|
108
|
+
} else if (args[i] === "--binary") {
|
|
109
|
+
flags.useBinary = true;
|
|
110
|
+
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
111
|
+
flags.showHelp = true;
|
|
112
|
+
} else {
|
|
113
|
+
flags.remainingArgs.push(args[i]);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return flags;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function runWithDocker(env, args) {
|
|
121
|
+
console.log("Using Docker to run midnight indexer...");
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
// Pull the latest image
|
|
125
|
+
await pullDockerImage();
|
|
126
|
+
|
|
127
|
+
// Run the container
|
|
128
|
+
return runDockerContainer(env, args);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error("Failed to run with Docker:", error.message);
|
|
131
|
+
console.log("Falling back to binary execution...");
|
|
132
|
+
return runWithBinary(env, args);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Sets appropriate defaults for binary execution (localhost-based)
|
|
138
|
+
* @param {Object} env - Original environment variables
|
|
139
|
+
* @returns {Object} Environment variables with localhost defaults
|
|
140
|
+
*/
|
|
141
|
+
function setBinaryDefaults(env) {
|
|
142
|
+
const binaryEnv = { ...env };
|
|
143
|
+
|
|
144
|
+
// Set localhost-based defaults for binary execution
|
|
145
|
+
if (!binaryEnv.LEDGER_NETWORK_ID) {
|
|
146
|
+
binaryEnv.LEDGER_NETWORK_ID = "Undeployed";
|
|
147
|
+
}
|
|
148
|
+
if (!binaryEnv.SUBSTRATE_NODE_WS_URL) {
|
|
149
|
+
binaryEnv.SUBSTRATE_NODE_WS_URL = "ws://localhost:9944";
|
|
150
|
+
}
|
|
151
|
+
if (!binaryEnv.FEATURES_WALLET_ENABLED) {
|
|
152
|
+
binaryEnv.FEATURES_WALLET_ENABLED = "true";
|
|
153
|
+
}
|
|
154
|
+
if (!binaryEnv.APP__INFRA__PROOF_SERVER__URL) {
|
|
155
|
+
binaryEnv.APP__INFRA__PROOF_SERVER__URL = "http://localhost:6300";
|
|
156
|
+
}
|
|
157
|
+
if (!binaryEnv.APP__INFRA__NODE__URL) {
|
|
158
|
+
binaryEnv.APP__INFRA__NODE__URL = "ws://localhost:9944";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return binaryEnv;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function runWithBinary(env, args) {
|
|
165
|
+
console.log("Using binary to run midnight indexer...");
|
|
166
|
+
|
|
167
|
+
// Check for required environment variable
|
|
168
|
+
if (!env.APP__INFRA__SECRET) {
|
|
169
|
+
console.error("Error: APP__INFRA__SECRET environment variable is required");
|
|
170
|
+
console.log("Please set APP__INFRA__SECRET=<your_secret> and run again");
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!checkIfBinaryExists()) {
|
|
175
|
+
console.log("Binary not found, downloading...");
|
|
176
|
+
await binary();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Set appropriate localhost defaults for binary execution
|
|
180
|
+
const binaryEnv = setBinaryDefaults(env);
|
|
181
|
+
|
|
182
|
+
return runMidnightIndexer(binaryEnv, args);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function main(args) {
|
|
186
|
+
const flags = parseFlags(args);
|
|
187
|
+
const env = process.env;
|
|
188
|
+
|
|
189
|
+
// Show help if requested
|
|
190
|
+
if (flags.showHelp) {
|
|
191
|
+
showUsage();
|
|
192
|
+
process.exit(0);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// If both flags are provided, show error
|
|
196
|
+
if (flags.useDocker && flags.useBinary) {
|
|
197
|
+
console.error(
|
|
198
|
+
"Error: Cannot use both --docker and --binary flags simultaneously",
|
|
199
|
+
);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// If --docker flag is explicitly provided
|
|
204
|
+
if (flags.useDocker) {
|
|
205
|
+
const dockerAvailable = await checkIfDockerExists();
|
|
206
|
+
if (!dockerAvailable) {
|
|
207
|
+
console.error("Error: Docker is not installed or not available");
|
|
208
|
+
console.log("Please install Docker or use the --binary flag");
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Check for required environment variable
|
|
213
|
+
if (!env.APP__INFRA__SECRET) {
|
|
214
|
+
console.error(
|
|
215
|
+
"Error: APP__INFRA__SECRET environment variable is required",
|
|
216
|
+
);
|
|
217
|
+
console.log(
|
|
218
|
+
"Please set APP__INFRA__SECRET=<your_secret> when running with --docker",
|
|
219
|
+
);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return runWithDocker(env, flags.remainingArgs);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// If --binary flag is explicitly provided
|
|
227
|
+
if (flags.useBinary) {
|
|
228
|
+
if (!isBinarySupported()) {
|
|
229
|
+
console.error(
|
|
230
|
+
"Error: Binary execution is not supported on this platform",
|
|
231
|
+
);
|
|
232
|
+
console.log(
|
|
233
|
+
"Please use --docker flag instead, or run without flags to use Docker automatically",
|
|
234
|
+
);
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
237
|
+
return runWithBinary(env, flags.remainingArgs);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// No explicit flag provided - determine best execution method
|
|
241
|
+
const dockerAvailable = await checkIfDockerExists();
|
|
242
|
+
const binarySupported = isBinarySupported();
|
|
243
|
+
|
|
244
|
+
// If binary is not supported on this platform, try Docker
|
|
245
|
+
if (!binarySupported) {
|
|
246
|
+
if (!dockerAvailable) {
|
|
247
|
+
console.error(
|
|
248
|
+
"Error: Binary execution is not supported on this platform and Docker is not installed or available",
|
|
249
|
+
);
|
|
250
|
+
console.log(
|
|
251
|
+
"Please install Docker or ensure your platform is supported for binary execution",
|
|
252
|
+
);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
console.log(
|
|
256
|
+
"Binary execution not supported on this platform - using Docker",
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
// Check for required environment variable
|
|
260
|
+
if (!env.APP__INFRA__SECRET) {
|
|
261
|
+
console.error(
|
|
262
|
+
"Error: APP__INFRA__SECRET environment variable is required",
|
|
263
|
+
);
|
|
264
|
+
console.log("Please set APP__INFRA__SECRET=<your_secret> and run again");
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return runWithDocker(env, flags.remainingArgs);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// On supported platforms, prompt user if Docker is available
|
|
272
|
+
if (dockerAvailable) {
|
|
273
|
+
const useDocker = await promptUserForDockerChoice();
|
|
274
|
+
if (useDocker) {
|
|
275
|
+
// Check for required environment variable
|
|
276
|
+
if (!env.APP__INFRA__SECRET) {
|
|
277
|
+
console.error(
|
|
278
|
+
"Error: APP__INFRA__SECRET environment variable is required",
|
|
279
|
+
);
|
|
280
|
+
console.log(
|
|
281
|
+
"Please set APP__INFRA__SECRET=<your_secret> and run again",
|
|
282
|
+
);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
return runWithDocker(env, flags.remainingArgs);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Default to binary execution (only on supported platforms)
|
|
290
|
+
return runWithBinary(env, flags.remainingArgs);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Handle unhandled promise rejections
|
|
294
|
+
process.on("unhandledRejection", (error) => {
|
|
295
|
+
console.error("Unhandled promise rejection:", error);
|
|
296
|
+
process.exit(1);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// Handle SIGINT (Ctrl+C) gracefully
|
|
300
|
+
process.on("SIGINT", () => {
|
|
301
|
+
console.log("\nShutting down gracefully...");
|
|
302
|
+
process.exit(0);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
main(process.argv.slice(2));
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@paimaexample/npm-midnight-indexer",
|
|
3
|
+
"version": "0.3.15",
|
|
4
|
+
"description": "Downloads and runs the Midnight Indexer. It needs a running Midnight Node",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"npm-midnight-indexer": "index.js"
|
|
11
|
+
},
|
|
12
|
+
"author": "Paima Studios",
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"supportedPlatforms": [
|
|
15
|
+
"linux-arm64",
|
|
16
|
+
"linux-amd64",
|
|
17
|
+
"macos-arm64"
|
|
18
|
+
],
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"axios": "^1.10.0",
|
|
21
|
+
"axios-proxy-builder": "^0.1.2",
|
|
22
|
+
"extract-zip": "^2.0.1"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Executes the midnight-indexer binary as a child process
|
|
6
|
+
* @param {Object} env - Environment variables to pass to the child process
|
|
7
|
+
* @param {Array} args - Optional arguments to pass to the binary
|
|
8
|
+
* @returns {ChildProcess} The spawned child process
|
|
9
|
+
*/
|
|
10
|
+
function runMidnightIndexer(env = process.env, args = []) {
|
|
11
|
+
const binaryPath = path.join(__dirname, 'indexer-standalone', 'indexer-standalone');
|
|
12
|
+
|
|
13
|
+
console.log(`Starting midnight-indexer binary at: ${binaryPath}`);
|
|
14
|
+
|
|
15
|
+
const childProcess = spawn(binaryPath, args, {
|
|
16
|
+
env: env,
|
|
17
|
+
stdio: 'inherit', // Inherit stdin, stdout, stderr from parent process
|
|
18
|
+
cwd: path.join(__dirname, 'indexer-standalone') // Run from inside the midnight-indexer directory
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
childProcess.on('spawn', () => {
|
|
22
|
+
console.log(`midnight-indexer process spawned with PID: ${childProcess.pid}`);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
childProcess.on('error', (error) => {
|
|
26
|
+
console.error('Failed to start midnight-indexer:', error);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
childProcess.on('exit', (code, signal) => {
|
|
30
|
+
if (code !== null) {
|
|
31
|
+
console.log(`midnight-indexer process exited with code: ${code}`);
|
|
32
|
+
} else {
|
|
33
|
+
console.log(`midnight-indexer process terminated by signal: ${signal}`);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return childProcess;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { runMidnightIndexer };
|