Spaces:
Running
Running
itszubariel commited on
Commit ·
563b757
1
Parent(s): 0b02bab
convert server to typescript
Browse files- Dockerfile +3 -3
- package.json +10 -2
- server.js → src/server.ts +60 -56
- tsconfig.json +13 -0
Dockerfile
CHANGED
|
@@ -1,10 +1,10 @@
|
|
| 1 |
FROM node:20-slim
|
| 2 |
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
| 3 |
-
|
| 4 |
-
RUN curl -L https://github.com/zbrlang/zbr/raw/debug-binary/debug-zbr -o /usr/local/bin/zbr && chmod +x /usr/local/bin/zbr
|
| 5 |
WORKDIR /app
|
| 6 |
COPY package*.json ./
|
| 7 |
RUN npm install
|
| 8 |
COPY . .
|
|
|
|
| 9 |
EXPOSE 7860
|
| 10 |
-
CMD ["
|
|
|
|
| 1 |
FROM node:20-slim
|
| 2 |
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
| 3 |
+
RUN curl -L https://github.com/zbrlang/zbr/releases/latest/download/zbr -o /usr/local/bin/zbr && chmod +x /usr/local/bin/zbr
|
|
|
|
| 4 |
WORKDIR /app
|
| 5 |
COPY package*.json ./
|
| 6 |
RUN npm install
|
| 7 |
COPY . .
|
| 8 |
+
RUN npm run build
|
| 9 |
EXPOSE 7860
|
| 10 |
+
CMD ["node", "dist/server.js"]
|
package.json
CHANGED
|
@@ -2,17 +2,25 @@
|
|
| 2 |
"name": "zbr-server",
|
| 3 |
"version": "1.0.0",
|
| 4 |
"description": "",
|
| 5 |
-
"main": "server.js",
|
| 6 |
"scripts": {
|
| 7 |
"test": "echo \"Error: no test specified\" && exit 1",
|
| 8 |
-
"start": "node server.js"
|
|
|
|
|
|
|
| 9 |
},
|
| 10 |
"keywords": [],
|
| 11 |
"author": "",
|
| 12 |
"license": "ISC",
|
| 13 |
"type": "commonjs",
|
| 14 |
"dependencies": {
|
|
|
|
|
|
|
| 15 |
"express": "^4.21.0",
|
|
|
|
| 16 |
"ws": "^8.21.0"
|
|
|
|
|
|
|
|
|
|
| 17 |
}
|
| 18 |
}
|
|
|
|
| 2 |
"name": "zbr-server",
|
| 3 |
"version": "1.0.0",
|
| 4 |
"description": "",
|
| 5 |
+
"main": "dist/server.js",
|
| 6 |
"scripts": {
|
| 7 |
"test": "echo \"Error: no test specified\" && exit 1",
|
| 8 |
+
"start": "node dist/server.js",
|
| 9 |
+
"build": "tsc",
|
| 10 |
+
"dev": "ts-node src/server.ts"
|
| 11 |
},
|
| 12 |
"keywords": [],
|
| 13 |
"author": "",
|
| 14 |
"license": "ISC",
|
| 15 |
"type": "commonjs",
|
| 16 |
"dependencies": {
|
| 17 |
+
"@types/express": "^4.17.21",
|
| 18 |
+
"@types/node": "^20.12.7",
|
| 19 |
"express": "^4.21.0",
|
| 20 |
+
"typescript": "^5.4.5",
|
| 21 |
"ws": "^8.21.0"
|
| 22 |
+
},
|
| 23 |
+
"devDependencies": {
|
| 24 |
+
"ts-node": "^10.9.2"
|
| 25 |
}
|
| 26 |
}
|
server.js → src/server.ts
RENAMED
|
@@ -1,20 +1,37 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
const app = express();
|
| 8 |
app.use(express.json({ limit: '10mb' }));
|
| 9 |
|
| 10 |
const ZBR_SECRET = process.env.ZBR_SECRET;
|
| 11 |
-
const processes = new Map();
|
| 12 |
-
|
| 13 |
-
// Discord Gateway Connectivity Test
|
| 14 |
-
const ws = new WebSocket('wss://gateway.discord.gg/?v=10&encoding=json');
|
| 15 |
-
ws.on('open', () => console.log('Discord WebSocket: REACHABLE'));
|
| 16 |
-
ws.on('error', (e) => console.log('Discord WebSocket: BLOCKED -', e.message));
|
| 17 |
-
ws.on('close', () => console.log('Discord WebSocket: closed'));
|
| 18 |
|
| 19 |
try {
|
| 20 |
const version = execSync('/usr/local/bin/zbr --version').toString();
|
|
@@ -23,62 +40,46 @@ try {
|
|
| 23 |
console.error("Failed to execute ZBR binary:", e);
|
| 24 |
}
|
| 25 |
|
| 26 |
-
const auth = (req, res, next) => {
|
| 27 |
if (req.path === '/health') return next();
|
| 28 |
-
if (req.headers['x-zbr-secret'] !== ZBR_SECRET)
|
|
|
|
|
|
|
| 29 |
next();
|
| 30 |
};
|
| 31 |
|
| 32 |
app.use(auth);
|
| 33 |
|
| 34 |
-
app.post('/run', (req, res) => {
|
| 35 |
-
console.log("Received payload keys:", Object.keys(req.body));
|
| 36 |
-
console.log("Commands received:", req.body.commands?.length);
|
| 37 |
-
console.log("Full .env received:", req.body.envFile);
|
| 38 |
-
|
| 39 |
const { botId, discordId, envFile, zbrJson, commands, dbBase64 } = req.body;
|
| 40 |
const key = `${discordId}-${botId}`;
|
| 41 |
if (processes.has(key)) return res.status(400).send('Bot already running');
|
| 42 |
|
| 43 |
const tempDir = `/tmp/zbr-${discordId}-${botId}`;
|
| 44 |
fs.mkdirSync(tempDir, { recursive: true });
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
fs.writeFileSync(
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
const jsonPath = path.join(tempDir, 'zbr.json');
|
| 51 |
-
fs.writeFileSync(jsonPath, zbrJson);
|
| 52 |
-
console.log("Wrote zbr.json to:", jsonPath, "Size:", fs.statSync(jsonPath).size);
|
| 53 |
-
|
| 54 |
const commandsDir = path.join(tempDir, 'commands');
|
| 55 |
fs.mkdirSync(commandsDir, { recursive: true });
|
| 56 |
commands.forEach(cmd => {
|
| 57 |
-
|
| 58 |
-
fs.writeFileSync(cmdPath, cmd.content);
|
| 59 |
-
console.log("Wrote command to:", cmdPath, "Size:", fs.statSync(cmdPath).size);
|
| 60 |
-
console.log("Content of", cmd.filename, ":", cmd.content);
|
| 61 |
-
});
|
| 62 |
-
|
| 63 |
-
const dbPath = path.join(tempDir, 'zbr.db');
|
| 64 |
-
fs.writeFileSync(dbPath, Buffer.from(dbBase64, 'base64'));
|
| 65 |
-
console.log("Wrote zbr.db to:", dbPath, "Size:", fs.statSync(dbPath).size);
|
| 66 |
-
|
| 67 |
-
console.log("Directory listing", tempDir, ":", fs.readdirSync(tempDir));
|
| 68 |
-
console.log("Directory listing", commandsDir, ":", fs.readdirSync(commandsDir));
|
| 69 |
-
|
| 70 |
-
const spawnCmd = '/usr/local/bin/zbr';
|
| 71 |
-
console.log("Spawning process:", spawnCmd, "in", tempDir);
|
| 72 |
-
const child = spawn(spawnCmd, [], {
|
| 73 |
-
cwd: tempDir,
|
| 74 |
-
stdio: ['ignore', 'pipe', 'pipe']
|
| 75 |
});
|
| 76 |
|
| 77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
|
| 79 |
-
child.stdout
|
| 80 |
-
|
| 81 |
-
|
|
|
|
|
|
|
|
|
|
| 82 |
|
| 83 |
processes.set(key, { child, tempDir });
|
| 84 |
|
|
@@ -89,7 +90,7 @@ app.post('/run', (req, res) => {
|
|
| 89 |
res.json({ success: true, status: 'running' });
|
| 90 |
});
|
| 91 |
|
| 92 |
-
function stopBot(key) {
|
| 93 |
const proc = processes.get(key);
|
| 94 |
if (!proc) return false;
|
| 95 |
proc.child.kill();
|
|
@@ -98,17 +99,20 @@ function stopBot(key) {
|
|
| 98 |
return true;
|
| 99 |
}
|
| 100 |
|
| 101 |
-
app.post('/stop', (req, res) => {
|
| 102 |
const { botId, discordId } = req.body;
|
| 103 |
-
if (stopBot(`${discordId}-${botId}`))
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
| 105 |
});
|
| 106 |
|
| 107 |
-
app.get('/status/:discordId/:botId', (req, res) => {
|
| 108 |
const { discordId, botId } = req.params;
|
| 109 |
res.json({ status: processes.has(`${discordId}-${botId}`) ? 'running' : 'stopped' });
|
| 110 |
});
|
| 111 |
|
| 112 |
-
app.get('/health', (req, res) => res.json({ ok: true }));
|
| 113 |
|
| 114 |
app.listen(7860, () => console.log('Server running on port 7860'));
|
|
|
|
| 1 |
+
import express, { Request, Response, NextFunction } from 'express';
|
| 2 |
+
import { spawn, execSync, ChildProcess } from 'child_process';
|
| 3 |
+
import fs from 'fs';
|
| 4 |
+
import path from 'path';
|
| 5 |
+
|
| 6 |
+
interface BotProcess {
|
| 7 |
+
child: ChildProcess;
|
| 8 |
+
tempDir: string;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
interface Command {
|
| 12 |
+
filename: string;
|
| 13 |
+
content: string;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
interface RunRequestBody {
|
| 17 |
+
botId: string;
|
| 18 |
+
discordId: string;
|
| 19 |
+
envFile: string;
|
| 20 |
+
zbrJson: string;
|
| 21 |
+
commands: Command[];
|
| 22 |
+
dbBase64: string;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
interface StopRequestBody {
|
| 26 |
+
botId: string;
|
| 27 |
+
discordId: string;
|
| 28 |
+
}
|
| 29 |
|
| 30 |
const app = express();
|
| 31 |
app.use(express.json({ limit: '10mb' }));
|
| 32 |
|
| 33 |
const ZBR_SECRET = process.env.ZBR_SECRET;
|
| 34 |
+
const processes = new Map<string, BotProcess>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
try {
|
| 37 |
const version = execSync('/usr/local/bin/zbr --version').toString();
|
|
|
|
| 40 |
console.error("Failed to execute ZBR binary:", e);
|
| 41 |
}
|
| 42 |
|
| 43 |
+
const auth = (req: Request, res: Response, next: NextFunction): void | Response => {
|
| 44 |
if (req.path === '/health') return next();
|
| 45 |
+
if (req.headers['x-zbr-secret'] !== ZBR_SECRET) {
|
| 46 |
+
return res.status(401).send('Unauthorized');
|
| 47 |
+
}
|
| 48 |
next();
|
| 49 |
};
|
| 50 |
|
| 51 |
app.use(auth);
|
| 52 |
|
| 53 |
+
app.post('/run', (req: Request<{}, any, RunRequestBody>, res: Response) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
const { botId, discordId, envFile, zbrJson, commands, dbBase64 } = req.body;
|
| 55 |
const key = `${discordId}-${botId}`;
|
| 56 |
if (processes.has(key)) return res.status(400).send('Bot already running');
|
| 57 |
|
| 58 |
const tempDir = `/tmp/zbr-${discordId}-${botId}`;
|
| 59 |
fs.mkdirSync(tempDir, { recursive: true });
|
| 60 |
+
|
| 61 |
+
fs.writeFileSync(path.join(tempDir, '.env'), envFile);
|
| 62 |
+
fs.writeFileSync(path.join(tempDir, 'zbr.json'), zbrJson);
|
| 63 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
const commandsDir = path.join(tempDir, 'commands');
|
| 65 |
fs.mkdirSync(commandsDir, { recursive: true });
|
| 66 |
commands.forEach(cmd => {
|
| 67 |
+
fs.writeFileSync(path.join(commandsDir, cmd.filename), cmd.content);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
});
|
| 69 |
|
| 70 |
+
fs.writeFileSync(path.join(tempDir, 'zbr.db'), Buffer.from(dbBase64, 'base64'));
|
| 71 |
+
|
| 72 |
+
const child = spawn('/usr/local/bin/zbr', [], {
|
| 73 |
+
cwd: tempDir,
|
| 74 |
+
stdio: ['ignore', 'pipe', 'pipe']
|
| 75 |
+
});
|
| 76 |
|
| 77 |
+
if (child.stdout) {
|
| 78 |
+
child.stdout.on('data', (data) => console.log(data.toString()));
|
| 79 |
+
}
|
| 80 |
+
if (child.stderr) {
|
| 81 |
+
child.stderr.on('data', (data) => console.error(data.toString()));
|
| 82 |
+
}
|
| 83 |
|
| 84 |
processes.set(key, { child, tempDir });
|
| 85 |
|
|
|
|
| 90 |
res.json({ success: true, status: 'running' });
|
| 91 |
});
|
| 92 |
|
| 93 |
+
function stopBot(key: string): boolean {
|
| 94 |
const proc = processes.get(key);
|
| 95 |
if (!proc) return false;
|
| 96 |
proc.child.kill();
|
|
|
|
| 99 |
return true;
|
| 100 |
}
|
| 101 |
|
| 102 |
+
app.post('/stop', (req: Request<{}, any, StopRequestBody>, res: Response) => {
|
| 103 |
const { botId, discordId } = req.body;
|
| 104 |
+
if (stopBot(`${discordId}-${botId}`)) {
|
| 105 |
+
res.json({ success: true, status: 'stopped' });
|
| 106 |
+
} else {
|
| 107 |
+
res.status(404).send('Bot not running');
|
| 108 |
+
}
|
| 109 |
});
|
| 110 |
|
| 111 |
+
app.get('/status/:discordId/:botId', (req: Request, res: Response) => {
|
| 112 |
const { discordId, botId } = req.params;
|
| 113 |
res.json({ status: processes.has(`${discordId}-${botId}`) ? 'running' : 'stopped' });
|
| 114 |
});
|
| 115 |
|
| 116 |
+
app.get('/health', (req: Request, res: Response) => res.json({ ok: true }));
|
| 117 |
|
| 118 |
app.listen(7860, () => console.log('Server running on port 7860'));
|
tsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"target": "ES2020",
|
| 4 |
+
"module": "commonjs",
|
| 5 |
+
"outDir": "./dist",
|
| 6 |
+
"rootDir": "./src",
|
| 7 |
+
"strict": true,
|
| 8 |
+
"esModuleInterop": true,
|
| 9 |
+
"skipLibCheck": true,
|
| 10 |
+
"forceConsistentCasingInFileNames": true
|
| 11 |
+
},
|
| 12 |
+
"include": ["src/**/*"]
|
| 13 |
+
}
|