@effectstream/npm-midnight-indexer 0.3.99
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 +245 -0
- package/binary.js +102 -0
- package/docker.js +164 -0
- package/index.js +316 -0
- package/indexer-standalone/config.yaml +44 -0
- package/package.json +25 -0
- package/run_midnight_indexer.js +160 -0
package/README.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Midnight Indexer
|
|
2
|
+
|
|
3
|
+
A Node.js package that downloads and runs the
|
|
4
|
+
[Midnight](https://midnight.network) Indexer. The indexer requires a running
|
|
5
|
+
Midnight Node to function properly.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
This package provides flexible execution options for the Midnight Indexer:
|
|
10
|
+
|
|
11
|
+
- **Docker mode**: Runs the indexer in a Docker container (recommended)
|
|
12
|
+
- **Binary mode**: Downloads and runs the native binary locally (Linux, Windows,
|
|
13
|
+
macOS arm64)
|
|
14
|
+
|
|
15
|
+
### Platform Support
|
|
16
|
+
|
|
17
|
+
| Platform | Docker | Binary |
|
|
18
|
+
| -------- | ------ | ------ |
|
|
19
|
+
| macOS | ✅ Yes | ✅ Yes |
|
|
20
|
+
| Linux | ✅ Yes | ✅ Yes |
|
|
21
|
+
| Windows | ❌ No | ✅ Yes |
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install npm-midnight-indexer
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Prerequisites
|
|
30
|
+
|
|
31
|
+
### For Docker Mode
|
|
32
|
+
|
|
33
|
+
- Docker installed and running
|
|
34
|
+
- `APP__INFRA__SECRET` environment variable (required)
|
|
35
|
+
|
|
36
|
+
### For Binary Mode
|
|
37
|
+
|
|
38
|
+
- Supported platform (Linux/macOS ARM64)
|
|
39
|
+
- Local Midnight Node and Proof Server running on standard ports. But they can
|
|
40
|
+
be set using env variables
|
|
41
|
+
- `APP__INFRA__SECRET` environment variable (required)
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
### Command Line Options
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
node index.js [options] [args...]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Options:**
|
|
52
|
+
|
|
53
|
+
- `--docker` - Force Docker execution
|
|
54
|
+
- `--binary` - Force binary execution (available on Linux, Windows, macOS arm64)
|
|
55
|
+
- `--help` - Show help information
|
|
56
|
+
|
|
57
|
+
**Examples:**
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Force Docker usage
|
|
61
|
+
APP__INFRA__SECRET=mysecret node index.js --docker
|
|
62
|
+
|
|
63
|
+
# Force binary usage (Linux/Windows/macOS arm64)
|
|
64
|
+
APP__INFRA__SECRET=mysecret node index.js --binary
|
|
65
|
+
|
|
66
|
+
# Interactive mode - prompts for Docker vs binary choice
|
|
67
|
+
APP__INFRA__SECRET=mysecret node index.js
|
|
68
|
+
|
|
69
|
+
# Show help
|
|
70
|
+
node index.js --help
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Environment Variables
|
|
74
|
+
|
|
75
|
+
The indexer supports several environment variables with different defaults based
|
|
76
|
+
on execution mode:
|
|
77
|
+
|
|
78
|
+
| Variable | Required | Docker Default | Binary Default | Description |
|
|
79
|
+
| ------------------------------- | -------- | -------------------------- | ----------------------- | ------------------------------ |
|
|
80
|
+
| `APP__INFRA__SECRET` | Yes | - | - | Secret key for the application |
|
|
81
|
+
| `LEDGER_NETWORK_ID` | No | `Undeployed` | `Undeployed` | Ledger network identifier |
|
|
82
|
+
| `SUBSTRATE_NODE_WS_URL` | No | `ws://node:9944` | `ws://localhost:9944` | Substrate node WebSocket URL |
|
|
83
|
+
| `FEATURES_WALLET_ENABLED` | No | `true` | `true` | Enable wallet features |
|
|
84
|
+
| `APP__INFRA__PROOF_SERVER__URL` | No | `http://proof-server:6300` | `http://localhost:6300` | Proof server URL |
|
|
85
|
+
| `APP__INFRA__NODE__URL` | No | `ws://node:9944` | `ws://localhost:9944` | Node WebSocket URL |
|
|
86
|
+
|
|
87
|
+
### Environment Variable Examples
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Minimal Docker setup
|
|
91
|
+
APP__INFRA__SECRET=A5F07FCAC914A181EC0998CCCA68312DA5F07FCAC914A181EC0998CCCA68312D \
|
|
92
|
+
node index.js --docker
|
|
93
|
+
|
|
94
|
+
# Custom configuration
|
|
95
|
+
APP__INFRA__SECRET=mysecret \
|
|
96
|
+
LEDGER_NETWORK_ID=CustomNetwork \
|
|
97
|
+
SUBSTRATE_NODE_WS_URL=ws://localhost:9944 \
|
|
98
|
+
APP__INFRA__PROOF_SERVER__URL=http://localhost:6300 \
|
|
99
|
+
node index.js --docker
|
|
100
|
+
|
|
101
|
+
# Binary mode (uses localhost defaults automatically)
|
|
102
|
+
APP__INFRA__SECRET=mysecret node index.js --binary
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Docker Mode Details
|
|
106
|
+
|
|
107
|
+
When using Docker mode, the indexer:
|
|
108
|
+
|
|
109
|
+
1. **Pulls the latest image**: `midnightntwrk/indexer-standalone`
|
|
110
|
+
2. **Maps port 8088**: Container port 8088 → Host port 8088
|
|
111
|
+
3. **Uses container networking**: Defaults work with Docker Compose setups
|
|
112
|
+
4. **Auto-cleanup**: Removes existing containers before starting new ones
|
|
113
|
+
|
|
114
|
+
### Docker Command Generated
|
|
115
|
+
|
|
116
|
+
The package generates Docker commands similar to:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
docker run --rm --name midnight-local-indexer \
|
|
120
|
+
-p 8088:8088 \
|
|
121
|
+
-e LEDGER_NETWORK_ID=Undeployed \
|
|
122
|
+
-e SUBSTRATE_NODE_WS_URL=ws://node:9944 \
|
|
123
|
+
-e APP__INFRA__SECRET=A5F07FCAC914A181EC0998CCCA68312DA5F07FCAC914A181EC0998CCCA68312D \
|
|
124
|
+
-e FEATURES_WALLET_ENABLED=true \
|
|
125
|
+
-e APP__INFRA__PROOF_SERVER__URL=http://proof-server:6300 \
|
|
126
|
+
-e APP__INFRA__NODE__URL=ws://node:9944 \
|
|
127
|
+
midnightntwrk/indexer-standalone
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Binary Mode Details
|
|
131
|
+
|
|
132
|
+
When using binary mode, the indexer:
|
|
133
|
+
|
|
134
|
+
1. **Downloads platform-specific binary** if not already present
|
|
135
|
+
2. **Uses localhost URLs** for all service connections
|
|
136
|
+
3. **Runs natively** on the host system
|
|
137
|
+
|
|
138
|
+
## Path Resolution
|
|
139
|
+
|
|
140
|
+
The indexer relies on two user-supplied file paths and **both are interpreted
|
|
141
|
+
relative to the process’s current working directory (CWD)** when they are not
|
|
142
|
+
absolute:
|
|
143
|
+
|
|
144
|
+
| Configuration location | Purpose | If relative, resolved against |
|
|
145
|
+
| ---------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------- |
|
|
146
|
+
| `CONFIG_FILE` environment variable | Location of the YAML config file | The directory you launch `node index.js …` from (or the Docker `WORKDIR`) |
|
|
147
|
+
| `infra.storage.cnn_url` in YAML | SQLite database used by the standalone build | The CWD at runtime of the indexer process |
|
|
148
|
+
|
|
149
|
+
### Practical tips
|
|
150
|
+
|
|
151
|
+
1. Prefer absolute paths when you need to be explicit.
|
|
152
|
+
2. In **binary mode** this package sets the CWD to the bundled
|
|
153
|
+
`indexer-standalone` folder, so a default `cnn_url: "./indexer.sqlite"` ends
|
|
154
|
+
up next to the binary.
|
|
155
|
+
3. In **Docker mode** the image’s `WORKDIR` is `/opt/indexer-standalone`.
|
|
156
|
+
Bind-mount volumes accordingly if you want the database file somewhere else.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### Supported Binary Platforms
|
|
161
|
+
|
|
162
|
+
- Linux ARM64 (`linux-arm64`)
|
|
163
|
+
- Linux AMD64 (`linux-amd64`)
|
|
164
|
+
- macOS ARM64 (`macos-arm64`)
|
|
165
|
+
|
|
166
|
+
## Troubleshooting
|
|
167
|
+
|
|
168
|
+
### Common Issues
|
|
169
|
+
|
|
170
|
+
**"Docker is not installed or not available"**
|
|
171
|
+
|
|
172
|
+
- Install Docker Desktop or Docker Engine
|
|
173
|
+
- Ensure Docker is running
|
|
174
|
+
- Check Docker is accessible from command line: `docker --version`
|
|
175
|
+
|
|
176
|
+
**"APP__INFRA__SECRET environment variable is required"**
|
|
177
|
+
|
|
178
|
+
- Set the secret: `export APP__INFRA__SECRET=your_secret_here`
|
|
179
|
+
- Or pass inline: `APP__INFRA__SECRET=your_secret node index.js --docker`
|
|
180
|
+
- Required for both Docker and binary execution modes
|
|
181
|
+
|
|
182
|
+
**"Binary execution is only supported on macOS ARM64"**
|
|
183
|
+
|
|
184
|
+
- Use `--docker` flag or run without flags to use Docker automatically
|
|
185
|
+
- Install Docker if not already installed
|
|
186
|
+
|
|
187
|
+
**"Failed to start midnight-indexer"**
|
|
188
|
+
|
|
189
|
+
- Check if ports 8088, 6300, 9944 are available
|
|
190
|
+
- Verify Midnight Node is running and accessible
|
|
191
|
+
- Check environment variables are correctly set
|
|
192
|
+
|
|
193
|
+
### Logs and Debugging
|
|
194
|
+
|
|
195
|
+
The indexer outputs detailed logs including:
|
|
196
|
+
|
|
197
|
+
- Download progress for binaries
|
|
198
|
+
- Docker pull progress
|
|
199
|
+
- Process IDs and status
|
|
200
|
+
- Error messages with suggestions
|
|
201
|
+
|
|
202
|
+
## Development
|
|
203
|
+
|
|
204
|
+
### Package Structure
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
npm-midnight-indexer/
|
|
208
|
+
├── index.js # Main entry point
|
|
209
|
+
├── binary.js # Binary download logic
|
|
210
|
+
├── docker.js # Docker execution logic
|
|
211
|
+
├── run_midnight_indexer.js # Binary execution logic
|
|
212
|
+
├── package.json # Package configuration
|
|
213
|
+
└── README.md # This file
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Testing
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Test Docker detection
|
|
220
|
+
node -e "const { checkIfDockerExists } = require('./docker.js'); checkIfDockerExists().then(console.log)"
|
|
221
|
+
|
|
222
|
+
# Test help
|
|
223
|
+
node index.js --help
|
|
224
|
+
|
|
225
|
+
# Test platform detection
|
|
226
|
+
node -e "console.log('Platform:', require('os').platform())"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
ISC
|
|
232
|
+
|
|
233
|
+
## Support
|
|
234
|
+
|
|
235
|
+
For issues related to:
|
|
236
|
+
|
|
237
|
+
- **This package**: Open an issue in the repository
|
|
238
|
+
- **Midnight Protocol**: Visit [midnight.network](https://midnight.network)
|
|
239
|
+
- **Docker**: Check [Docker documentation](https://docs.docker.com/)
|
|
240
|
+
|
|
241
|
+
## Related Links
|
|
242
|
+
|
|
243
|
+
- [Midnight Network](https://midnight.network)
|
|
244
|
+
- [Docker Hub - Midnight Indexer](https://hub.docker.com/r/midnightntwrk/indexer-standalone)
|
|
245
|
+
- [Node.js](https://nodejs.org/)
|
package/binary.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
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
|
+
const CURRENT_BINARY_VERSION = "v2.1.4";
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
@returns {string} The platform and architecture of the current machine. Example: "linux-amd64"
|
|
11
|
+
*/
|
|
12
|
+
function getPlatform() {
|
|
13
|
+
const platform = os.platform();
|
|
14
|
+
const arch = os.arch();
|
|
15
|
+
|
|
16
|
+
if (platform === "darwin") {
|
|
17
|
+
// For macOS, return the full platform-arch combination
|
|
18
|
+
if (arch === "x64") {
|
|
19
|
+
return "macos-amd64"; // Will not be in supportedPlatforms, so will fall back to Docker
|
|
20
|
+
} else {
|
|
21
|
+
return `macos-${arch}`;
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
// For Linux and other platforms, only arch is needed
|
|
25
|
+
if (arch === "x64") {
|
|
26
|
+
return `${platform}-amd64`;
|
|
27
|
+
}
|
|
28
|
+
return `${platform}-${arch}`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/*
|
|
33
|
+
@returns {string} The URL to download the binary for the current platform.
|
|
34
|
+
*/
|
|
35
|
+
function getBinaryUrl() {
|
|
36
|
+
const platform = getPlatform();
|
|
37
|
+
const supportedPlatforms = require("./package.json").supportedPlatforms;
|
|
38
|
+
// Check if platform is supported
|
|
39
|
+
if (!supportedPlatforms.includes(platform)) {
|
|
40
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
41
|
+
}
|
|
42
|
+
return `https://paima-midnight.nyc3.cdn.digitaloceanspaces.com/binaries/indexer-standalone-${platform}-${CURRENT_BINARY_VERSION}.zip`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/*
|
|
46
|
+
@returns {Promise<void>} Downloads and saves the binary for the current platform.
|
|
47
|
+
*/
|
|
48
|
+
async function downloadAndSaveBinary() {
|
|
49
|
+
const url = getBinaryUrl();
|
|
50
|
+
try {
|
|
51
|
+
console.error(`Downloading... ${url}`);
|
|
52
|
+
const response = await axios.get(url, { responseType: "stream" });
|
|
53
|
+
const writer = fs.createWriteStream(
|
|
54
|
+
path.join(__dirname, "indexer-standalone.zip"),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
response.data.pipe(writer);
|
|
58
|
+
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
writer.on("finish", resolve);
|
|
61
|
+
writer.on("error", reject);
|
|
62
|
+
});
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error("Error downloading binary:", error);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/*
|
|
70
|
+
@returns {Promise<void>} Unzips the binary for the current platform.
|
|
71
|
+
*/
|
|
72
|
+
async function unzipBinary() {
|
|
73
|
+
const dir = path.join(__dirname, "indexer-standalone");
|
|
74
|
+
if (!fs.existsSync(dir)) {
|
|
75
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
76
|
+
}
|
|
77
|
+
await extract(path.join(__dirname, "indexer-standalone.zip"), { dir });
|
|
78
|
+
fs.unlinkSync(path.join(__dirname, "indexer-standalone.zip"));
|
|
79
|
+
|
|
80
|
+
const platform = getPlatform();
|
|
81
|
+
const parts = platform.split("-");
|
|
82
|
+
if (parts[0] === "linux") {
|
|
83
|
+
fs.chmodSync(
|
|
84
|
+
path.join(
|
|
85
|
+
__dirname,
|
|
86
|
+
"indexer-standalone",
|
|
87
|
+
`indexer-standalone-${platform}`,
|
|
88
|
+
),
|
|
89
|
+
0o755,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function binary() {
|
|
95
|
+
await downloadAndSaveBinary();
|
|
96
|
+
await unzipBinary();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = {
|
|
100
|
+
binary,
|
|
101
|
+
getPlatform,
|
|
102
|
+
};
|
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,316 @@
|
|
|
1
|
+
const { binary, getPlatform } = 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
|
+
const platform = getPlatform();
|
|
13
|
+
const parts = platform.split("-");
|
|
14
|
+
const binaryName = `indexer-standalone-${platform}`;
|
|
15
|
+
return fs.existsSync(
|
|
16
|
+
path.join(__dirname, "indexer-standalone", binaryName),
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Checks if the current platform supports binary execution
|
|
22
|
+
* @returns {boolean} True if binary execution is supported
|
|
23
|
+
*/
|
|
24
|
+
function isBinarySupported() {
|
|
25
|
+
const platform = os.platform();
|
|
26
|
+
const arch = os.arch();
|
|
27
|
+
|
|
28
|
+
// Check if the current platform-arch combination is supported
|
|
29
|
+
const supportedPlatforms = require("./package.json").supportedPlatforms;
|
|
30
|
+
|
|
31
|
+
let platformString;
|
|
32
|
+
if (platform === "darwin") {
|
|
33
|
+
platformString = arch === "x64" ? "macos-amd64" : `macos-${arch}`;
|
|
34
|
+
} else {
|
|
35
|
+
platformString = arch === "x64"
|
|
36
|
+
? `${platform}-amd64`
|
|
37
|
+
: `${platform}-${arch}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return supportedPlatforms.includes(platformString);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Prompts the user to choose between Docker and binary execution
|
|
45
|
+
* @returns {Promise<boolean>} True if user chooses Docker, false for binary
|
|
46
|
+
*/
|
|
47
|
+
function promptUserForDockerChoice() {
|
|
48
|
+
const rl = readline.createInterface({
|
|
49
|
+
input: process.stdin,
|
|
50
|
+
output: process.stdout,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
rl.question(
|
|
55
|
+
"Docker is available. Would you like to use Docker instead of downloading the binary? (y/n): ",
|
|
56
|
+
(answer) => {
|
|
57
|
+
rl.close();
|
|
58
|
+
resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Shows usage information
|
|
66
|
+
*/
|
|
67
|
+
function showUsage() {
|
|
68
|
+
console.log(`
|
|
69
|
+
Usage: node index.js [options] [args...]
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
--docker Force use of Docker container
|
|
73
|
+
--binary Force use of binary execution (supports Linux and macOS arm64)
|
|
74
|
+
--help Show this help message
|
|
75
|
+
|
|
76
|
+
Environment Variables:
|
|
77
|
+
APP__INFRA__SECRET Required: Secret key for the application
|
|
78
|
+
LEDGER_NETWORK_ID Optional: Ledger network ID (default: Undeployed)
|
|
79
|
+
SUBSTRATE_NODE_WS_URL Optional: Substrate node WebSocket URL
|
|
80
|
+
(Docker default: ws://node:9944, Binary default: ws://localhost:9944)
|
|
81
|
+
FEATURES_WALLET_ENABLED Optional: Enable wallet features (default: true)
|
|
82
|
+
APP__INFRA__PROOF_SERVER__URL Optional: Proof server URL
|
|
83
|
+
(Docker default: http://proof-server:6300, Binary default: http://localhost:6300)
|
|
84
|
+
APP__INFRA__NODE__URL Optional: Node URL
|
|
85
|
+
(Docker default: ws://node:9944, Binary default: ws://localhost:9944)
|
|
86
|
+
|
|
87
|
+
Note: macOS Intel users will automatically use Docker. macOS arm64 supports both binary and Docker execution.
|
|
88
|
+
|
|
89
|
+
Examples:
|
|
90
|
+
APP__INFRA__SECRET=mysecret node index.js --docker # Use Docker
|
|
91
|
+
APP__INFRA__SECRET=mysecret node index.js --binary # Use binary (if supported)
|
|
92
|
+
APP__INFRA__SECRET=mysecret node index.js # Interactive mode (or auto-Docker on unsupported platforms)
|
|
93
|
+
node index.js --help # Show this help
|
|
94
|
+
`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Parses command line arguments to extract flags and remaining args
|
|
99
|
+
* @param {Array} args - Command line arguments
|
|
100
|
+
* @returns {Object} Object containing useDocker, useBinary flags and remaining args
|
|
101
|
+
*/
|
|
102
|
+
function parseFlags(args) {
|
|
103
|
+
const flags = {
|
|
104
|
+
useDocker: false,
|
|
105
|
+
useBinary: false,
|
|
106
|
+
showHelp: false,
|
|
107
|
+
remainingArgs: [],
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
for (let i = 0; i < args.length; i++) {
|
|
111
|
+
if (args[i] === "--docker") {
|
|
112
|
+
flags.useDocker = true;
|
|
113
|
+
} else if (args[i] === "--binary") {
|
|
114
|
+
flags.useBinary = true;
|
|
115
|
+
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
116
|
+
flags.showHelp = true;
|
|
117
|
+
} else {
|
|
118
|
+
flags.remainingArgs.push(args[i]);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return flags;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function runWithDocker(env, args) {
|
|
126
|
+
console.log("Using Docker to run midnight indexer...");
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
// Pull the latest image
|
|
130
|
+
await pullDockerImage();
|
|
131
|
+
|
|
132
|
+
// Run the container
|
|
133
|
+
return runDockerContainer(env, args);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error("Failed to run with Docker:", error.message);
|
|
136
|
+
console.log("Falling back to binary execution...");
|
|
137
|
+
return runWithBinary(env, args);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Sets appropriate defaults for binary execution (localhost-based)
|
|
143
|
+
* @param {Object} env - Original environment variables
|
|
144
|
+
* @returns {Object} Environment variables with localhost defaults
|
|
145
|
+
*/
|
|
146
|
+
function setBinaryDefaults(env) {
|
|
147
|
+
const binaryEnv = { ...env };
|
|
148
|
+
|
|
149
|
+
// Set localhost-based defaults for binary execution
|
|
150
|
+
if (!binaryEnv.LEDGER_NETWORK_ID) {
|
|
151
|
+
binaryEnv.LEDGER_NETWORK_ID = "Undeployed";
|
|
152
|
+
}
|
|
153
|
+
if (!binaryEnv.SUBSTRATE_NODE_WS_URL) {
|
|
154
|
+
binaryEnv.SUBSTRATE_NODE_WS_URL = "ws://localhost:9944";
|
|
155
|
+
}
|
|
156
|
+
if (!binaryEnv.FEATURES_WALLET_ENABLED) {
|
|
157
|
+
binaryEnv.FEATURES_WALLET_ENABLED = "true";
|
|
158
|
+
}
|
|
159
|
+
if (!binaryEnv.APP__INFRA__PROOF_SERVER__URL) {
|
|
160
|
+
binaryEnv.APP__INFRA__PROOF_SERVER__URL = "http://localhost:6300";
|
|
161
|
+
}
|
|
162
|
+
if (!binaryEnv.APP__INFRA__NODE__URL) {
|
|
163
|
+
binaryEnv.APP__INFRA__NODE__URL = "ws://localhost:9944";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return binaryEnv;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function runWithBinary(env, args) {
|
|
170
|
+
console.log("Using binary to run midnight indexer...");
|
|
171
|
+
|
|
172
|
+
// Check for required environment variable
|
|
173
|
+
if (!env.APP__INFRA__SECRET) {
|
|
174
|
+
console.error("Error: APP__INFRA__SECRET environment variable is required");
|
|
175
|
+
console.log("Please set APP__INFRA__SECRET=<your_secret> and run again");
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!checkIfBinaryExists()) {
|
|
180
|
+
console.log("Binary not found, downloading...");
|
|
181
|
+
await binary();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Set appropriate localhost defaults for binary execution
|
|
185
|
+
const binaryEnv = setBinaryDefaults(env);
|
|
186
|
+
|
|
187
|
+
return runMidnightIndexer(binaryEnv, args);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function main(args) {
|
|
191
|
+
const flags = parseFlags(args);
|
|
192
|
+
const env = process.env;
|
|
193
|
+
|
|
194
|
+
// Show help if requested
|
|
195
|
+
if (flags.showHelp) {
|
|
196
|
+
showUsage();
|
|
197
|
+
process.exit(0);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// If both flags are provided, show error
|
|
201
|
+
if (flags.useDocker && flags.useBinary) {
|
|
202
|
+
console.error(
|
|
203
|
+
"Error: Cannot use both --docker and --binary flags simultaneously",
|
|
204
|
+
);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// If --docker flag is explicitly provided
|
|
209
|
+
if (flags.useDocker) {
|
|
210
|
+
const dockerAvailable = await checkIfDockerExists();
|
|
211
|
+
if (!dockerAvailable) {
|
|
212
|
+
console.error("Error: Docker is not installed or not available");
|
|
213
|
+
console.log("Please install Docker or use the --binary flag");
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Check for required environment variable
|
|
218
|
+
if (!env.APP__INFRA__SECRET) {
|
|
219
|
+
console.error(
|
|
220
|
+
"Error: APP__INFRA__SECRET environment variable is required",
|
|
221
|
+
);
|
|
222
|
+
console.log(
|
|
223
|
+
"Please set APP__INFRA__SECRET=<your_secret> when running with --docker",
|
|
224
|
+
);
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return runWithDocker(env, flags.remainingArgs);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// If --binary flag is explicitly provided
|
|
232
|
+
if (flags.useBinary) {
|
|
233
|
+
if (!isBinarySupported()) {
|
|
234
|
+
console.error(
|
|
235
|
+
"Error: Binary execution is not supported on this platform for: " +
|
|
236
|
+
os.platform() +
|
|
237
|
+
" " +
|
|
238
|
+
os.arch(),
|
|
239
|
+
);
|
|
240
|
+
console.log(
|
|
241
|
+
"Please use --docker flag instead, or run without flags to use Docker automatically",
|
|
242
|
+
);
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
return runWithBinary(env, flags.remainingArgs);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// No explicit flag provided - determine best execution method
|
|
249
|
+
const dockerAvailable = await checkIfDockerExists();
|
|
250
|
+
const binarySupported = isBinarySupported();
|
|
251
|
+
|
|
252
|
+
// If binary is not supported on this platform, try Docker
|
|
253
|
+
if (!binarySupported) {
|
|
254
|
+
if (!dockerAvailable) {
|
|
255
|
+
console.error(
|
|
256
|
+
"Error: Binary execution is not supported on this platform and Docker is not installed or available. For: " +
|
|
257
|
+
os.platform() +
|
|
258
|
+
" " +
|
|
259
|
+
os.arch(),
|
|
260
|
+
);
|
|
261
|
+
console.log(
|
|
262
|
+
"Please install Docker or ensure your platform is supported for binary execution",
|
|
263
|
+
);
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
console.log(
|
|
267
|
+
"Binary execution not supported on this platform - using Docker",
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
// Check for required environment variable
|
|
271
|
+
if (!env.APP__INFRA__SECRET) {
|
|
272
|
+
console.error(
|
|
273
|
+
"Error: APP__INFRA__SECRET environment variable is required",
|
|
274
|
+
);
|
|
275
|
+
console.log("Please set APP__INFRA__SECRET=<your_secret> and run again");
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return runWithDocker(env, flags.remainingArgs);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// On supported platforms, prompt user if Docker is available
|
|
283
|
+
if (dockerAvailable) {
|
|
284
|
+
const useDocker = await promptUserForDockerChoice();
|
|
285
|
+
if (useDocker) {
|
|
286
|
+
// Check for required environment variable
|
|
287
|
+
if (!env.APP__INFRA__SECRET) {
|
|
288
|
+
console.error(
|
|
289
|
+
"Error: APP__INFRA__SECRET environment variable is required",
|
|
290
|
+
);
|
|
291
|
+
console.log(
|
|
292
|
+
"Please set APP__INFRA__SECRET=<your_secret> and run again",
|
|
293
|
+
);
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
return runWithDocker(env, flags.remainingArgs);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Default to binary execution (only on supported platforms)
|
|
301
|
+
return runWithBinary(env, flags.remainingArgs);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Handle unhandled promise rejections
|
|
305
|
+
process.on("unhandledRejection", (error) => {
|
|
306
|
+
console.error("Unhandled promise rejection:", error);
|
|
307
|
+
process.exit(1);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// Handle SIGINT (Ctrl+C) gracefully
|
|
311
|
+
process.on("SIGINT", () => {
|
|
312
|
+
console.log("\nShutting down gracefully...");
|
|
313
|
+
process.exit(0);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
main(process.argv.slice(2));
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
run_migrations: true
|
|
2
|
+
network_id: &network_id "Undeployed"
|
|
3
|
+
|
|
4
|
+
chain_indexer_application:
|
|
5
|
+
network_id: *network_id
|
|
6
|
+
blocks_buffer: 60
|
|
7
|
+
save_zswap_state_after: 1000
|
|
8
|
+
caught_up_max_distance: 60
|
|
9
|
+
caught_up_leeway: 30
|
|
10
|
+
|
|
11
|
+
wallet_indexer_application:
|
|
12
|
+
network_id: *network_id
|
|
13
|
+
active_wallets_repeat_delay: "100ms"
|
|
14
|
+
active_wallets_ttl: "30m"
|
|
15
|
+
transaction_batch_size: 10
|
|
16
|
+
# Number of cores by default.
|
|
17
|
+
# parallelism:
|
|
18
|
+
|
|
19
|
+
infra:
|
|
20
|
+
node:
|
|
21
|
+
url: "ws://localhost:9944"
|
|
22
|
+
reconnect_max_delay: "10s" # 10ms, 100ms, 1s, 10s
|
|
23
|
+
reconnect_max_attempts: 30 # Roughly 5m
|
|
24
|
+
|
|
25
|
+
storage:
|
|
26
|
+
cnn_url: "./data/indexer.sqlite"
|
|
27
|
+
|
|
28
|
+
api:
|
|
29
|
+
address: "127.0.0.1"
|
|
30
|
+
port: 8088
|
|
31
|
+
request_body_limit: "1MiB"
|
|
32
|
+
max_complexity: 200
|
|
33
|
+
max_depth: 15
|
|
34
|
+
network_id: *network_id
|
|
35
|
+
|
|
36
|
+
telemetry:
|
|
37
|
+
tracing:
|
|
38
|
+
enabled: false
|
|
39
|
+
service_name: "indexer"
|
|
40
|
+
otlp_exporter_endpoint: "http://localhost:4317"
|
|
41
|
+
metrics:
|
|
42
|
+
enabled: false
|
|
43
|
+
address: "0.0.0.0"
|
|
44
|
+
port: 9000
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@effectstream/npm-midnight-indexer",
|
|
3
|
+
"version": "0.3.99",
|
|
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
|
+
"js-yaml": "^4.1.0"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
const { spawn } = require("child_process");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const yaml = require("js-yaml");
|
|
5
|
+
const { getPlatform } = require("./binary");
|
|
6
|
+
/**
|
|
7
|
+
* Resolves the SQLite database path using the midnight-indexer configuration rules
|
|
8
|
+
* @param {Object} env - Environment variables
|
|
9
|
+
* @param {string} workingDir - The working directory where the indexer runs
|
|
10
|
+
* @returns {string|null} The resolved SQLite database path, or null if not found
|
|
11
|
+
*/
|
|
12
|
+
function resolveSqlitePath(env, workingDir) {
|
|
13
|
+
// First check the APP__INFRA__STORAGE__CNN_URL environment variable
|
|
14
|
+
const envCnnUrl = env.APP__INFRA__STORAGE__CNN_URL;
|
|
15
|
+
if (envCnnUrl) {
|
|
16
|
+
console.log(`Found SQLite path from environment variable: ${envCnnUrl}`);
|
|
17
|
+
return envCnnUrl;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Determine config file path using the configuration resolution rules
|
|
21
|
+
let configPath;
|
|
22
|
+
if (env.CONFIG_FILE) {
|
|
23
|
+
configPath = path.isAbsolute(env.CONFIG_FILE)
|
|
24
|
+
? env.CONFIG_FILE
|
|
25
|
+
: path.resolve(workingDir, env.CONFIG_FILE);
|
|
26
|
+
} else {
|
|
27
|
+
// Fall back to config.yaml in the current working directory
|
|
28
|
+
configPath = path.join(workingDir, "config.yaml");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log(`Looking for config file at: ${configPath}`);
|
|
32
|
+
|
|
33
|
+
// Check if config file exists
|
|
34
|
+
if (!fs.existsSync(configPath)) {
|
|
35
|
+
console.warn(`Config file not found at: ${configPath}`);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// Parse the YAML config file
|
|
41
|
+
const configContent = fs.readFileSync(configPath, "utf8");
|
|
42
|
+
const config = yaml.load(configContent);
|
|
43
|
+
|
|
44
|
+
// Extract the cnn_url from infra.storage
|
|
45
|
+
const cnnUrl = config?.infra?.storage?.cnn_url;
|
|
46
|
+
if (!cnnUrl) {
|
|
47
|
+
console.warn("No cnn_url found in config file under infra.storage");
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log(`Found SQLite path from config file: ${cnnUrl}`);
|
|
52
|
+
|
|
53
|
+
// If the path is relative, resolve it against the binary working directory (indexer-standalone)
|
|
54
|
+
if (!path.isAbsolute(cnnUrl)) {
|
|
55
|
+
const resolvedPath = path.resolve(workingDir, cnnUrl);
|
|
56
|
+
console.log(`Resolved relative path to: ${resolvedPath}`);
|
|
57
|
+
return resolvedPath;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return cnnUrl;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error(`Failed to parse config file: ${error.message}`);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Handles the --clean flag by deleting the SQLite database file
|
|
69
|
+
* @param {Object} env - Environment variables
|
|
70
|
+
* @param {string} workingDir - The working directory where the indexer runs
|
|
71
|
+
*/
|
|
72
|
+
function handleCleanFlag(env, workingDir) {
|
|
73
|
+
console.log("Processing --clean flag...");
|
|
74
|
+
|
|
75
|
+
const sqlitePath = resolveSqlitePath(env, workingDir);
|
|
76
|
+
|
|
77
|
+
if (!sqlitePath) {
|
|
78
|
+
console.warn("Could not resolve SQLite database path. Skipping cleanup.");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Handle sqlite:// URLs and extract the file path
|
|
83
|
+
let filePath = sqlitePath;
|
|
84
|
+
if (sqlitePath.startsWith("sqlite://")) {
|
|
85
|
+
filePath = sqlitePath.replace("sqlite://", "");
|
|
86
|
+
} else if (sqlitePath.startsWith("sqlite:///")) {
|
|
87
|
+
filePath = sqlitePath.replace("sqlite:///", "/");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log(`Attempting to clean SQLite database at: ${filePath}`);
|
|
91
|
+
|
|
92
|
+
if (fs.existsSync(filePath)) {
|
|
93
|
+
try {
|
|
94
|
+
fs.unlinkSync(filePath);
|
|
95
|
+
console.log(`Successfully deleted SQLite database: ${filePath}`);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error(`Failed to delete SQLite database: ${error.message}`);
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
console.log(
|
|
101
|
+
`SQLite database does not exist (will be created fresh): ${filePath}`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Executes the midnight-indexer binary as a child process
|
|
108
|
+
* @param {Object} env - Environment variables to pass to the child process
|
|
109
|
+
* @param {Array} args - Optional arguments to pass to the binary
|
|
110
|
+
* @returns {ChildProcess} The spawned child process
|
|
111
|
+
*/
|
|
112
|
+
function runMidnightIndexer(env = process.env, args = []) {
|
|
113
|
+
const platform = getPlatform();
|
|
114
|
+
const parts = platform.split("-");
|
|
115
|
+
const binaryName = `indexer-standalone-${platform}`;
|
|
116
|
+
const binaryPath = path.join(
|
|
117
|
+
__dirname,
|
|
118
|
+
"indexer-standalone",
|
|
119
|
+
binaryName,
|
|
120
|
+
);
|
|
121
|
+
const workingDir = path.join(__dirname, "indexer-standalone");
|
|
122
|
+
|
|
123
|
+
// Check for --clean flag and handle it
|
|
124
|
+
const cleanFlagIndex = args.indexOf("--clean");
|
|
125
|
+
if (cleanFlagIndex !== -1) {
|
|
126
|
+
handleCleanFlag(env, workingDir);
|
|
127
|
+
// Remove the --clean flag from args since the binary doesn't expect it
|
|
128
|
+
args.splice(cleanFlagIndex, 1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log(`Starting midnight-indexer binary at: ${binaryPath}`);
|
|
132
|
+
|
|
133
|
+
const childProcess = spawn(binaryPath, args, {
|
|
134
|
+
env: env,
|
|
135
|
+
stdio: "inherit", // Inherit stdin, stdout, stderr from parent process
|
|
136
|
+
cwd: workingDir, // Run from inside the indexer-standalone directory
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
childProcess.on("spawn", () => {
|
|
140
|
+
console.log(
|
|
141
|
+
`midnight-indexer process spawned with PID: ${childProcess.pid}`,
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
childProcess.on("error", (error) => {
|
|
146
|
+
console.error("Failed to start midnight-indexer:", error);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
childProcess.on("exit", (code, signal) => {
|
|
150
|
+
if (code !== null) {
|
|
151
|
+
console.log(`midnight-indexer process exited with code: ${code}`);
|
|
152
|
+
} else {
|
|
153
|
+
console.log(`midnight-indexer process terminated by signal: ${signal}`);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
return childProcess;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = { runMidnightIndexer };
|