wiki-plugin-allyabase 0.0.5 → 0.0.7
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/BUGFIXES.md +277 -0
- package/allyabase_setup.sh +18 -5
- package/package.json +1 -1
- package/server/server.js +269 -14
package/BUGFIXES.md
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# Allyabase Plugin - Critical Bug Fixes
|
|
2
|
+
|
|
3
|
+
## Date: February 9, 2026
|
|
4
|
+
|
|
5
|
+
### Critical Bug #1: Invalid Express Route Wildcard
|
|
6
|
+
|
|
7
|
+
**Problem:**
|
|
8
|
+
Multiple routes used the wildcard `*` syntax which is invalid in newer versions of path-to-regexp (used by Express):
|
|
9
|
+
|
|
10
|
+
```javascript
|
|
11
|
+
// Federation route (line 291)
|
|
12
|
+
app.use('/plugin/allyabase/federation/*', function(req, res, next) {
|
|
13
|
+
|
|
14
|
+
// Service proxy routes (line 633)
|
|
15
|
+
app.all(`/plugin/allyabase/${service}/*`, function(req, res) {
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This caused:
|
|
19
|
+
```
|
|
20
|
+
PathError: Missing parameter name at index 30: /plugin/allyabase/federation/*
|
|
21
|
+
PathError: Missing parameter name at index 25: /plugin/allyabase/julia/*
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Fix:**
|
|
25
|
+
Changed both to use regex patterns:
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
// Federation route (line 291)
|
|
29
|
+
app.use(/^\/plugin\/allyabase\/federation\/.*/, function(req, res, next) {
|
|
30
|
+
|
|
31
|
+
// Service proxy routes (line 633)
|
|
32
|
+
app.all(new RegExp(`^\\/plugin\\/allyabase\\/${service}\\/.*`), function(req, res) {
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Locations:**
|
|
36
|
+
- `server/server.js:291` - Federation route
|
|
37
|
+
- `server/server.js:633` - Service proxy routes (all 14 services)
|
|
38
|
+
|
|
39
|
+
**Impact:** Plugin would crash immediately on load, making all federation endpoints and service proxies unusable.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
### Critical Bug #2: Process Suicide in Docker Containers
|
|
44
|
+
|
|
45
|
+
**Problem:**
|
|
46
|
+
The `cleanupOrphanedProcesses()` function scans ports and kills any process using them. In Docker containers:
|
|
47
|
+
- PID 1 is always the main container process (the wiki server)
|
|
48
|
+
- When the plugin finds the wiki using a port, it tries to kill PID 1
|
|
49
|
+
- This kills the entire container
|
|
50
|
+
|
|
51
|
+
**Symptoms:**
|
|
52
|
+
```
|
|
53
|
+
[wiki-plugin-allyabase] Found process 1 using port 3005
|
|
54
|
+
[wiki-plugin-allyabase] Attempting to kill process 1...
|
|
55
|
+
[wiki-plugin-allyabase] Process 1 still running, sending SIGKILL...
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Followed by container crash.
|
|
59
|
+
|
|
60
|
+
**Fix #1: PID Protection**
|
|
61
|
+
Added safety checks to `killProcessByPid()`:
|
|
62
|
+
```javascript
|
|
63
|
+
// CRITICAL: Never kill PID 1 in Docker containers
|
|
64
|
+
if (pid === 1) {
|
|
65
|
+
console.log('⚠️ Skipping PID 1 (init process)');
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Also don't kill our own process
|
|
70
|
+
if (pid === process.pid) {
|
|
71
|
+
console.log('⚠️ Skipping PID ${pid} (this is us!)');
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Fix #2: Docker Detection**
|
|
77
|
+
Added Docker environment detection to skip port cleanup entirely:
|
|
78
|
+
```javascript
|
|
79
|
+
const isDocker = fs.existsSync('/.dockerenv') || fs.existsSync('/run/.containerenv');
|
|
80
|
+
|
|
81
|
+
if (isDocker) {
|
|
82
|
+
console.log('🐳 Detected Docker environment - skipping port cleanup');
|
|
83
|
+
console.log('In Docker, services should run in separate containers');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Location:** `server/server.js:63-100, 156-175`
|
|
89
|
+
|
|
90
|
+
**Rationale:**
|
|
91
|
+
In Docker/containerized environments:
|
|
92
|
+
- Each service runs in its own container
|
|
93
|
+
- Services don't share the same process space
|
|
94
|
+
- Attempting to kill processes by port is:
|
|
95
|
+
- Dangerous (can kill the wiki itself)
|
|
96
|
+
- Unnecessary (services are isolated)
|
|
97
|
+
- Won't work anyway (can't kill processes in other containers)
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Testing the Fixes
|
|
102
|
+
|
|
103
|
+
### Before Fix:
|
|
104
|
+
```bash
|
|
105
|
+
# Install allyabase plugin
|
|
106
|
+
docker exec -u node -w /home/node/lib/node_modules/wiki wiki-dave npm install wiki-plugin-allyabase
|
|
107
|
+
|
|
108
|
+
# Restart wiki
|
|
109
|
+
docker-compose restart wiki-dave
|
|
110
|
+
|
|
111
|
+
# Result: Wiki crashes immediately
|
|
112
|
+
# Logs show: Attempting to kill process 1... [crash]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### After Fix:
|
|
116
|
+
```bash
|
|
117
|
+
# Install allyabase plugin
|
|
118
|
+
docker exec -u node -w /home/node/lib/node_modules/wiki wiki-dave npm install wiki-plugin-allyabase
|
|
119
|
+
|
|
120
|
+
# Restart wiki
|
|
121
|
+
docker-compose restart wiki-dave
|
|
122
|
+
|
|
123
|
+
# Result: Wiki starts successfully
|
|
124
|
+
# Logs show:
|
|
125
|
+
# 🐳 Detected Docker environment - skipping port cleanup
|
|
126
|
+
# ✅ wiki-plugin-allyabase ready!
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Architecture Implications
|
|
132
|
+
|
|
133
|
+
### Original Design (Non-Docker):
|
|
134
|
+
```
|
|
135
|
+
┌─────────────────────────────┐
|
|
136
|
+
│ Single Machine/VM │
|
|
137
|
+
│ │
|
|
138
|
+
│ ┌──────────────────────┐ │
|
|
139
|
+
│ │ Wiki (PID 123) │ │
|
|
140
|
+
│ │ Port 3333 │ │
|
|
141
|
+
│ └──────────────────────┘ │
|
|
142
|
+
│ │
|
|
143
|
+
│ ┌──────────────────────┐ │
|
|
144
|
+
│ │ PM2 │ │
|
|
145
|
+
│ │ ├─ Fount (PID 456) │ │
|
|
146
|
+
│ │ ├─ BDO (PID 789) │ │
|
|
147
|
+
│ │ ├─ Joan (PID 101) │ │
|
|
148
|
+
│ │ └─ ...13 more │ │
|
|
149
|
+
│ └──────────────────────┘ │
|
|
150
|
+
│ │
|
|
151
|
+
│ Allyabase plugin can: │
|
|
152
|
+
│ - Kill orphaned PM2 │
|
|
153
|
+
│ - Kill processes on ports │
|
|
154
|
+
│ - Restart services │
|
|
155
|
+
└─────────────────────────────┘
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Docker Design (Current):
|
|
159
|
+
```
|
|
160
|
+
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
|
161
|
+
│ Wiki Container │ │ Fount Container │ │ BDO Container │
|
|
162
|
+
│ │ │ │ │ │
|
|
163
|
+
│ ┌──────────────┐ │ │ ┌──────────────┐ │ │ ┌──────────────┐ │
|
|
164
|
+
│ │ Wiki (PID 1) │ │ │ │ Fount (PID 1)│ │ │ │ BDO (PID 1) │ │
|
|
165
|
+
│ │ Port 3000 │ │ │ │ Port 3006 │ │ │ │ Port 3003 │ │
|
|
166
|
+
│ └──────────────┘ │ │ └──────────────┘ │ │ └──────────────┘ │
|
|
167
|
+
└──────────────────┘ └──────────────────┘ └──────────────────┘
|
|
168
|
+
│ │ │
|
|
169
|
+
└─────────────────────┴──────────────────────┘
|
|
170
|
+
│
|
|
171
|
+
Docker Network
|
|
172
|
+
|
|
173
|
+
Each container:
|
|
174
|
+
- Has its own PID 1 (init process)
|
|
175
|
+
- Isolated process space
|
|
176
|
+
- Cannot kill processes in other containers
|
|
177
|
+
- Managed by Docker Compose, not PM2
|
|
178
|
+
|
|
179
|
+
Allyabase plugin in Docker:
|
|
180
|
+
- ✅ Provides proxy routes to services
|
|
181
|
+
- ✅ Handles federation resolution
|
|
182
|
+
- ❌ Cannot manage service lifecycles (that's Docker's job)
|
|
183
|
+
- ❌ Cannot kill processes (dangerous and won't work)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Future Considerations
|
|
189
|
+
|
|
190
|
+
### Service Management in Docker
|
|
191
|
+
|
|
192
|
+
The plugin's service management features (launch, healthcheck) need rethinking for Docker:
|
|
193
|
+
|
|
194
|
+
**Current Approach (Non-Docker):**
|
|
195
|
+
- `POST /plugin/allyabase/launch` - Runs `allyabase_setup.sh` which spawns PM2
|
|
196
|
+
- PM2 manages all 14 services as child processes
|
|
197
|
+
- Plugin can kill/restart services
|
|
198
|
+
|
|
199
|
+
**Docker Approach Options:**
|
|
200
|
+
|
|
201
|
+
1. **Docker Compose (Recommended):**
|
|
202
|
+
```yaml
|
|
203
|
+
services:
|
|
204
|
+
wiki:
|
|
205
|
+
image: wiki-federation:latest
|
|
206
|
+
fount:
|
|
207
|
+
image: fount:latest
|
|
208
|
+
bdo:
|
|
209
|
+
image: bdo:latest
|
|
210
|
+
# ...etc
|
|
211
|
+
```
|
|
212
|
+
- Services managed by Docker Compose
|
|
213
|
+
- Plugin acts as pure proxy layer
|
|
214
|
+
- No lifecycle management in plugin
|
|
215
|
+
|
|
216
|
+
2. **Docker API Integration:**
|
|
217
|
+
- Plugin calls Docker API to start/stop containers
|
|
218
|
+
- Requires Docker socket mount
|
|
219
|
+
- More complex but gives control
|
|
220
|
+
|
|
221
|
+
3. **Hybrid (Current State):**
|
|
222
|
+
- Plugin works as proxy in Docker
|
|
223
|
+
- Manual service management via docker-compose CLI
|
|
224
|
+
- Plugin's /launch endpoint doesn't work in Docker (services already running)
|
|
225
|
+
|
|
226
|
+
### Recommendation
|
|
227
|
+
|
|
228
|
+
For Docker deployments:
|
|
229
|
+
1. Use docker-compose to manage all services
|
|
230
|
+
2. Allyabase plugin provides:
|
|
231
|
+
- ✅ Service proxy routes
|
|
232
|
+
- ✅ Federation resolution
|
|
233
|
+
- ✅ Healthcheck endpoint (checks if services respond)
|
|
234
|
+
- ❌ Launch/lifecycle management (handled by Docker)
|
|
235
|
+
|
|
236
|
+
For non-Docker deployments:
|
|
237
|
+
1. Original design works as intended
|
|
238
|
+
2. PM2 process management
|
|
239
|
+
3. Full lifecycle control via plugin
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Files Modified
|
|
244
|
+
|
|
245
|
+
1. **server/server.js**
|
|
246
|
+
- Line 291: Fixed Express federation route wildcard (`*` → regex)
|
|
247
|
+
- Line 633: Fixed Express service proxy route wildcards (all 14 services, `*` → regex)
|
|
248
|
+
- Lines 63-100: Added PID 1 protection and self-protection
|
|
249
|
+
- Lines 156-175: Added Docker detection and port cleanup skip
|
|
250
|
+
|
|
251
|
+
## Backward Compatibility
|
|
252
|
+
|
|
253
|
+
These changes are **fully backward compatible**:
|
|
254
|
+
|
|
255
|
+
- **Non-Docker environments:** Port cleanup still works (unless PID 1 or self)
|
|
256
|
+
- **Docker environments:** Automatically detected and handled safely
|
|
257
|
+
- **Existing functionality:** All proxy routes, federation, and endpoints unchanged
|
|
258
|
+
|
|
259
|
+
## Testing Checklist
|
|
260
|
+
|
|
261
|
+
- [ ] Plugin loads without crash in Docker
|
|
262
|
+
- [ ] Federation endpoints respond (no PathError)
|
|
263
|
+
- [ ] Wiki doesn't kill itself on startup
|
|
264
|
+
- [ ] Proxy routes work (e.g., `/plugin/allyabase/bdo/*`)
|
|
265
|
+
- [ ] Healthcheck endpoint works
|
|
266
|
+
- [ ] Docker detection logs appear
|
|
267
|
+
- [ ] Non-Docker deployment still works (if applicable)
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Related Issues
|
|
272
|
+
|
|
273
|
+
- Original crash report: Dave's wiki crashed when allyabase installed
|
|
274
|
+
- Error 1: `PathError: Missing parameter name at index 30`
|
|
275
|
+
- Error 2: `Attempting to kill process 1` followed by container death
|
|
276
|
+
|
|
277
|
+
Both issues now resolved.
|
package/allyabase_setup.sh
CHANGED
|
@@ -112,11 +112,11 @@ setup_ecosystem() {
|
|
|
112
112
|
declare -A service_ports=(
|
|
113
113
|
[julia]=3000
|
|
114
114
|
[continuebee]=2999
|
|
115
|
-
[fount]=
|
|
115
|
+
[fount]=3006
|
|
116
116
|
[bdo]=3003
|
|
117
117
|
[joan]=3004
|
|
118
118
|
[addie]=3005
|
|
119
|
-
[pref]=
|
|
119
|
+
[pref]=3002
|
|
120
120
|
[dolores]=3007
|
|
121
121
|
[prof]=3008
|
|
122
122
|
[covenant]=3011
|
|
@@ -168,9 +168,22 @@ main() {
|
|
|
168
168
|
setup_ecosystem
|
|
169
169
|
|
|
170
170
|
# Start with pm2 daemon (allows pm2 logs, pm2 status, etc.)
|
|
171
|
+
echo "Starting PM2 with ecosystem config..."
|
|
171
172
|
./node_modules/.bin/pm2 start ecosystem.config.js
|
|
172
173
|
|
|
173
|
-
#
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
# Wait a moment for services to start
|
|
175
|
+
echo "Waiting for services to initialize..."
|
|
176
|
+
sleep 5
|
|
177
|
+
|
|
178
|
+
# Show PM2 status
|
|
179
|
+
./node_modules/.bin/pm2 status
|
|
180
|
+
|
|
181
|
+
echo "✅ Allyabase services launched successfully"
|
|
182
|
+
echo "Services are running in PM2 daemon mode"
|
|
183
|
+
echo "Use 'pm2 status' to check service status"
|
|
184
|
+
echo "Use 'pm2 logs' to view logs"
|
|
185
|
+
|
|
186
|
+
# Don't block - exit cleanly so wiki plugin can track the setup script's PID
|
|
187
|
+
# PM2 daemon will keep services running in the background
|
|
188
|
+
exit 0
|
|
176
189
|
}; main
|
package/package.json
CHANGED
package/server/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
(function() {
|
|
2
|
-
const { exec } = require('child_process');
|
|
2
|
+
const { exec, spawn } = require('child_process');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const http = require('http');
|
|
5
5
|
const httpProxy = require('http-proxy');
|
|
@@ -26,12 +26,17 @@ const SERVICE_PORTS = {
|
|
|
26
26
|
sanora: 7243
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
// PID file for tracking PM2 process
|
|
30
|
+
const PM2_PID_FILE = path.join(__dirname, 'allyabase-pm2.pid');
|
|
31
|
+
|
|
29
32
|
let baseStatus = {
|
|
30
33
|
running: false,
|
|
31
34
|
services: {},
|
|
32
35
|
lastLaunch: null
|
|
33
36
|
};
|
|
34
37
|
|
|
38
|
+
let allyabaseProcess = null;
|
|
39
|
+
|
|
35
40
|
// Function to load wiki's owner.json for keypair and location emoji
|
|
36
41
|
function loadWikiKeypair() {
|
|
37
42
|
try {
|
|
@@ -55,6 +60,185 @@ function loadWikiKeypair() {
|
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
62
|
|
|
63
|
+
// Function to kill process by PID
|
|
64
|
+
function killProcessByPid(pid) {
|
|
65
|
+
try {
|
|
66
|
+
// CRITICAL: Never kill PID 1 in Docker containers - it's the main container process
|
|
67
|
+
if (pid === 1) {
|
|
68
|
+
console.log(`[wiki-plugin-allyabase] ⚠️ Skipping PID 1 (init process) - this is likely the wiki server itself in Docker`);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Also don't kill our own process
|
|
73
|
+
if (pid === process.pid) {
|
|
74
|
+
console.log(`[wiki-plugin-allyabase] ⚠️ Skipping PID ${pid} (this is us!)`);
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.log(`[wiki-plugin-allyabase] Attempting to kill process ${pid}...`);
|
|
79
|
+
process.kill(pid, 'SIGTERM');
|
|
80
|
+
|
|
81
|
+
// Wait a bit, then force kill if still running
|
|
82
|
+
setTimeout(() => {
|
|
83
|
+
try {
|
|
84
|
+
process.kill(pid, 0); // Check if still alive
|
|
85
|
+
console.log(`[wiki-plugin-allyabase] Process ${pid} still running, sending SIGKILL...`);
|
|
86
|
+
process.kill(pid, 'SIGKILL');
|
|
87
|
+
} catch (err) {
|
|
88
|
+
// Process is dead, which is what we want
|
|
89
|
+
console.log(`[wiki-plugin-allyabase] ✅ Process ${pid} terminated successfully`);
|
|
90
|
+
}
|
|
91
|
+
}, 2000);
|
|
92
|
+
|
|
93
|
+
return true;
|
|
94
|
+
} catch (err) {
|
|
95
|
+
if (err.code === 'ESRCH') {
|
|
96
|
+
console.log(`[wiki-plugin-allyabase] Process ${pid} does not exist`);
|
|
97
|
+
} else {
|
|
98
|
+
console.error(`[wiki-plugin-allyabase] Error killing process ${pid}:`, err.message);
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Function to find and kill process using a specific port
|
|
105
|
+
function killProcessByPort(port) {
|
|
106
|
+
return new Promise((resolve) => {
|
|
107
|
+
// Use lsof to find process using the port
|
|
108
|
+
exec(`lsof -ti tcp:${port}`, (err, stdout, stderr) => {
|
|
109
|
+
if (err || !stdout.trim()) {
|
|
110
|
+
console.log(`[wiki-plugin-allyabase] No process found using port ${port}`);
|
|
111
|
+
resolve(false);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const pid = parseInt(stdout.trim(), 10);
|
|
116
|
+
console.log(`[wiki-plugin-allyabase] Found process ${pid} using port ${port}`);
|
|
117
|
+
|
|
118
|
+
const killed = killProcessByPid(pid);
|
|
119
|
+
resolve(killed);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Function to stop PM2 and all managed services
|
|
125
|
+
async function stopPM2() {
|
|
126
|
+
console.log('[wiki-plugin-allyabase] Stopping PM2 and all services...');
|
|
127
|
+
|
|
128
|
+
return new Promise((resolve) => {
|
|
129
|
+
// Try to stop PM2 gracefully using pm2 kill
|
|
130
|
+
exec('pm2 kill', (err, stdout, stderr) => {
|
|
131
|
+
if (err) {
|
|
132
|
+
console.log(`[wiki-plugin-allyabase] PM2 kill failed (might not be running): ${err.message}`);
|
|
133
|
+
} else {
|
|
134
|
+
console.log(`[wiki-plugin-allyabase] PM2 killed successfully`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Clean up PID file
|
|
138
|
+
cleanupPidFile();
|
|
139
|
+
resolve();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Function to clean up orphaned processes from previous run
|
|
145
|
+
async function cleanupOrphanedProcesses() {
|
|
146
|
+
console.log('[wiki-plugin-allyabase] Checking for orphaned allyabase processes...');
|
|
147
|
+
|
|
148
|
+
// Check PID file for PM2 process
|
|
149
|
+
if (fs.existsSync(PM2_PID_FILE)) {
|
|
150
|
+
try {
|
|
151
|
+
const pidString = fs.readFileSync(PM2_PID_FILE, 'utf8').trim();
|
|
152
|
+
const pid = parseInt(pidString, 10);
|
|
153
|
+
|
|
154
|
+
console.log(`[wiki-plugin-allyabase] Found PID file with PID ${pid}`);
|
|
155
|
+
killProcessByPid(pid);
|
|
156
|
+
|
|
157
|
+
// Wait for process to die
|
|
158
|
+
await new Promise(resolve => setTimeout(resolve, 2500));
|
|
159
|
+
|
|
160
|
+
// Clean up PID file
|
|
161
|
+
fs.unlinkSync(PM2_PID_FILE);
|
|
162
|
+
console.log(`[wiki-plugin-allyabase] Cleaned up PID file`);
|
|
163
|
+
} catch (err) {
|
|
164
|
+
console.error(`[wiki-plugin-allyabase] Error reading PID file:`, err.message);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Stop PM2 if it's running (cleans up all managed services)
|
|
169
|
+
await stopPM2();
|
|
170
|
+
|
|
171
|
+
// Detect if we're in a Docker container
|
|
172
|
+
// In Docker, services run in separate containers, so port cleanup is unnecessary and dangerous
|
|
173
|
+
const isDocker = fs.existsSync('/.dockerenv') || fs.existsSync('/run/.containerenv');
|
|
174
|
+
|
|
175
|
+
if (isDocker) {
|
|
176
|
+
console.log('[wiki-plugin-allyabase] 🐳 Detected Docker environment - skipping port cleanup');
|
|
177
|
+
console.log('[wiki-plugin-allyabase] In Docker, services should run in separate containers, not as processes on ports');
|
|
178
|
+
console.log('[wiki-plugin-allyabase] Cleanup complete (Docker mode)');
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Fallback: kill any processes using our ports (only in non-Docker environments)
|
|
183
|
+
console.log('[wiki-plugin-allyabase] Cleaning up any processes on service ports...');
|
|
184
|
+
for (const [service, port] of Object.entries(SERVICE_PORTS)) {
|
|
185
|
+
await killProcessByPort(port);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Extra wait to ensure everything is dead
|
|
189
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
190
|
+
console.log('[wiki-plugin-allyabase] Cleanup complete');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Function to write PID file
|
|
194
|
+
function writePidFile(pid) {
|
|
195
|
+
try {
|
|
196
|
+
fs.writeFileSync(PM2_PID_FILE, pid.toString(), 'utf8');
|
|
197
|
+
console.log(`[wiki-plugin-allyabase] Wrote PID ${pid} to ${PM2_PID_FILE}`);
|
|
198
|
+
} catch (err) {
|
|
199
|
+
console.error(`[wiki-plugin-allyabase] Error writing PID file:`, err.message);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Function to clean up PID file
|
|
204
|
+
function cleanupPidFile() {
|
|
205
|
+
try {
|
|
206
|
+
if (fs.existsSync(PM2_PID_FILE)) {
|
|
207
|
+
fs.unlinkSync(PM2_PID_FILE);
|
|
208
|
+
console.log(`[wiki-plugin-allyabase] Cleaned up PID file`);
|
|
209
|
+
}
|
|
210
|
+
} catch (err) {
|
|
211
|
+
console.error(`[wiki-plugin-allyabase] Error cleaning up PID file:`, err.message);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Function to gracefully shutdown allyabase
|
|
216
|
+
async function shutdownAllyabase() {
|
|
217
|
+
console.log('[wiki-plugin-allyabase] Shutting down allyabase services...');
|
|
218
|
+
|
|
219
|
+
if (allyabaseProcess && !allyabaseProcess.killed) {
|
|
220
|
+
console.log(`[wiki-plugin-allyabase] Killing allyabase process ${allyabaseProcess.pid}...`);
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
allyabaseProcess.kill('SIGTERM');
|
|
224
|
+
|
|
225
|
+
// Force kill after timeout
|
|
226
|
+
setTimeout(() => {
|
|
227
|
+
if (allyabaseProcess && !allyabaseProcess.killed) {
|
|
228
|
+
console.log(`[wiki-plugin-allyabase] Force killing allyabase process...`);
|
|
229
|
+
allyabaseProcess.kill('SIGKILL');
|
|
230
|
+
}
|
|
231
|
+
}, 2000);
|
|
232
|
+
} catch (err) {
|
|
233
|
+
console.error(`[wiki-plugin-allyabase] Error killing allyabase:`, err.message);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Stop PM2
|
|
238
|
+
await stopPM2();
|
|
239
|
+
cleanupPidFile();
|
|
240
|
+
}
|
|
241
|
+
|
|
58
242
|
// Function to check if a port is responding
|
|
59
243
|
function checkPort(port) {
|
|
60
244
|
return new Promise((resolve) => {
|
|
@@ -112,6 +296,11 @@ async function healthcheck() {
|
|
|
112
296
|
async function startServer(params) {
|
|
113
297
|
const app = params.app;
|
|
114
298
|
|
|
299
|
+
console.log('🔗 wiki-plugin-allyabase starting...');
|
|
300
|
+
|
|
301
|
+
// Clean up any orphaned allyabase/PM2 processes from previous run
|
|
302
|
+
await cleanupOrphanedProcesses();
|
|
303
|
+
|
|
115
304
|
// Owner middleware to protect state-changing endpoints
|
|
116
305
|
const owner = function (req, res, next) {
|
|
117
306
|
if (!app.securityhandler.isAuthorized(req)) {
|
|
@@ -122,7 +311,8 @@ async function startServer(params) {
|
|
|
122
311
|
|
|
123
312
|
// CORS middleware for federation endpoints
|
|
124
313
|
// Allows cross-origin requests from other federated wikis
|
|
125
|
-
|
|
314
|
+
// Use regex to match all federation paths (Express doesn't support * wildcard in newer versions)
|
|
315
|
+
app.use(/^\/plugin\/allyabase\/federation\/.*/, function(req, res, next) {
|
|
126
316
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
127
317
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
128
318
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
@@ -440,7 +630,8 @@ async function startServer(params) {
|
|
|
440
630
|
// Create proxy routes for each service
|
|
441
631
|
Object.entries(SERVICE_PORTS).forEach(([service, port]) => {
|
|
442
632
|
// Proxy all methods (GET, POST, PUT, DELETE, etc.)
|
|
443
|
-
|
|
633
|
+
// Use regex pattern instead of wildcard to avoid PathError in newer path-to-regexp
|
|
634
|
+
app.all(new RegExp(`^\\/plugin\\/allyabase\\/${service}\\/.*`), function(req, res) {
|
|
444
635
|
const targetPath = req.url.replace(`/plugin/allyabase/${service}`, '');
|
|
445
636
|
console.log(`[PROXY] ${req.method} /plugin/allyabase/${service}${targetPath} -> http://localhost:${port}${targetPath}`);
|
|
446
637
|
console.log(`[PROXY] Headers:`, JSON.stringify(req.headers, null, 2));
|
|
@@ -476,25 +667,50 @@ async function startServer(params) {
|
|
|
476
667
|
console.log('Timestamp:', new Date().toISOString());
|
|
477
668
|
console.log('-'.repeat(80));
|
|
478
669
|
|
|
479
|
-
// Launch the setup script with spawn
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
670
|
+
// Launch the setup script with spawn
|
|
671
|
+
allyabaseProcess = spawn('bash', [scriptPath], {
|
|
672
|
+
stdio: ['ignore', 'pipe', 'pipe'], // Don't inherit stdio, capture it
|
|
673
|
+
env: process.env,
|
|
674
|
+
detached: false // Keep as child of this process
|
|
484
675
|
});
|
|
485
676
|
|
|
486
|
-
|
|
487
|
-
|
|
677
|
+
// Write PID file immediately after spawning
|
|
678
|
+
writePidFile(allyabaseProcess.pid);
|
|
679
|
+
|
|
680
|
+
// Log stdout
|
|
681
|
+
allyabaseProcess.stdout.on('data', (data) => {
|
|
682
|
+
const lines = data.toString().split('\n').filter(line => line.trim());
|
|
683
|
+
lines.forEach(line => {
|
|
684
|
+
console.log(`[allyabase:${allyabaseProcess.pid}] ${line}`);
|
|
685
|
+
});
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
// Log stderr
|
|
689
|
+
allyabaseProcess.stderr.on('data', (data) => {
|
|
690
|
+
console.error(`[allyabase:${allyabaseProcess.pid}] ERROR: ${data.toString().trim()}`);
|
|
488
691
|
});
|
|
489
692
|
|
|
490
|
-
|
|
693
|
+
// Handle process exit
|
|
694
|
+
allyabaseProcess.on('exit', (code, signal) => {
|
|
491
695
|
console.log('-'.repeat(80));
|
|
492
696
|
if (code === 0) {
|
|
493
697
|
console.log('✅ Allyabase setup completed successfully');
|
|
494
|
-
} else {
|
|
495
|
-
console.log(`⚠️ Allyabase setup exited with code: ${code}
|
|
698
|
+
} else if (code) {
|
|
699
|
+
console.log(`⚠️ Allyabase setup exited with code: ${code}`);
|
|
700
|
+
} else if (signal) {
|
|
701
|
+
console.log(`⚠️ Allyabase setup killed by signal: ${signal}`);
|
|
496
702
|
}
|
|
497
703
|
console.log('='.repeat(80));
|
|
704
|
+
|
|
705
|
+
// Clean up PID file when process exits
|
|
706
|
+
cleanupPidFile();
|
|
707
|
+
allyabaseProcess = null;
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
allyabaseProcess.on('error', (error) => {
|
|
711
|
+
console.error('❌ Error spawning allyabase process:', error);
|
|
712
|
+
cleanupPidFile();
|
|
713
|
+
allyabaseProcess = null;
|
|
498
714
|
});
|
|
499
715
|
|
|
500
716
|
baseStatus.lastLaunch = new Date().toISOString();
|
|
@@ -503,7 +719,8 @@ async function startServer(params) {
|
|
|
503
719
|
success: true,
|
|
504
720
|
message: 'Allyabase launch initiated',
|
|
505
721
|
timestamp: baseStatus.lastLaunch,
|
|
506
|
-
scriptPath: scriptPath
|
|
722
|
+
scriptPath: scriptPath,
|
|
723
|
+
pid: allyabaseProcess.pid
|
|
507
724
|
});
|
|
508
725
|
} catch (err) {
|
|
509
726
|
console.error('❌ Error launching allyabase:', err);
|
|
@@ -829,6 +1046,44 @@ async function startServer(params) {
|
|
|
829
1046
|
|
|
830
1047
|
// Add deployment routes
|
|
831
1048
|
deployment.addRoutes(params);
|
|
1049
|
+
|
|
1050
|
+
console.log('✅ wiki-plugin-allyabase ready!');
|
|
1051
|
+
|
|
1052
|
+
// Set up shutdown hooks to clean up allyabase/PM2 processes
|
|
1053
|
+
let isShuttingDown = false;
|
|
1054
|
+
|
|
1055
|
+
const handleShutdown = async (signal) => {
|
|
1056
|
+
if (isShuttingDown) {
|
|
1057
|
+
return; // Already shutting down
|
|
1058
|
+
}
|
|
1059
|
+
isShuttingDown = true;
|
|
1060
|
+
|
|
1061
|
+
console.log(`[wiki-plugin-allyabase] Received ${signal}, shutting down...`);
|
|
1062
|
+
await shutdownAllyabase();
|
|
1063
|
+
|
|
1064
|
+
// Give it a moment to clean up
|
|
1065
|
+
setTimeout(() => {
|
|
1066
|
+
console.log('[wiki-plugin-allyabase] Shutdown complete');
|
|
1067
|
+
// Don't call process.exit() here - let the parent process handle that
|
|
1068
|
+
}, 3000);
|
|
1069
|
+
};
|
|
1070
|
+
|
|
1071
|
+
// Register shutdown handlers (only once per process)
|
|
1072
|
+
if (!process.allyabaseShutdownRegistered) {
|
|
1073
|
+
process.allyabaseShutdownRegistered = true;
|
|
1074
|
+
|
|
1075
|
+
process.on('SIGINT', () => handleShutdown('SIGINT'));
|
|
1076
|
+
process.on('SIGTERM', () => handleShutdown('SIGTERM'));
|
|
1077
|
+
process.on('exit', () => {
|
|
1078
|
+
if (!isShuttingDown) {
|
|
1079
|
+
// Synchronous cleanup on exit
|
|
1080
|
+
console.log('[wiki-plugin-allyabase] Process exiting, cleaning up...');
|
|
1081
|
+
cleanupPidFile();
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
console.log('[wiki-plugin-allyabase] Shutdown handlers registered');
|
|
1086
|
+
}
|
|
832
1087
|
}
|
|
833
1088
|
|
|
834
1089
|
module.exports = { startServer };
|