@minecraft-docker/mcctl-api 1.7.6
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 +142 -0
- package/dist/app.d.ts +7 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +83 -0
- package/dist/app.js.map +1 -0
- package/dist/config/index.d.ts +32 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +124 -0
- package/dist/config/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/rcon.d.ts +35 -0
- package/dist/lib/rcon.d.ts.map +1 -0
- package/dist/lib/rcon.js +90 -0
- package/dist/lib/rcon.js.map +1 -0
- package/dist/plugins/auth.d.ts +32 -0
- package/dist/plugins/auth.d.ts.map +1 -0
- package/dist/plugins/auth.js +194 -0
- package/dist/plugins/auth.js.map +1 -0
- package/dist/plugins/swagger.d.ts +170 -0
- package/dist/plugins/swagger.d.ts.map +1 -0
- package/dist/plugins/swagger.js +211 -0
- package/dist/plugins/swagger.js.map +1 -0
- package/dist/routes/auth.d.ts +12 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +144 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/backup.d.ts +9 -0
- package/dist/routes/backup.d.ts.map +1 -0
- package/dist/routes/backup.js +271 -0
- package/dist/routes/backup.js.map +1 -0
- package/dist/routes/console.d.ts +6 -0
- package/dist/routes/console.d.ts.map +1 -0
- package/dist/routes/console.js +90 -0
- package/dist/routes/console.js.map +1 -0
- package/dist/routes/players.d.ts +9 -0
- package/dist/routes/players.d.ts.map +1 -0
- package/dist/routes/players.js +353 -0
- package/dist/routes/players.js.map +1 -0
- package/dist/routes/router.d.ts +10 -0
- package/dist/routes/router.d.ts.map +1 -0
- package/dist/routes/router.js +55 -0
- package/dist/routes/router.js.map +1 -0
- package/dist/routes/servers/actions.d.ts +6 -0
- package/dist/routes/servers/actions.d.ts.map +1 -0
- package/dist/routes/servers/actions.js +65 -0
- package/dist/routes/servers/actions.js.map +1 -0
- package/dist/routes/servers.d.ts +10 -0
- package/dist/routes/servers.d.ts.map +1 -0
- package/dist/routes/servers.js +403 -0
- package/dist/routes/servers.js.map +1 -0
- package/dist/routes/worlds.d.ts +10 -0
- package/dist/routes/worlds.d.ts.map +1 -0
- package/dist/routes/worlds.js +341 -0
- package/dist/routes/worlds.js.map +1 -0
- package/dist/schemas/backup.d.ts +47 -0
- package/dist/schemas/backup.d.ts.map +1 -0
- package/dist/schemas/backup.js +43 -0
- package/dist/schemas/backup.js.map +1 -0
- package/dist/schemas/player.d.ts +46 -0
- package/dist/schemas/player.d.ts.map +1 -0
- package/dist/schemas/player.js +46 -0
- package/dist/schemas/player.js.map +1 -0
- package/dist/schemas/router.d.ts +42 -0
- package/dist/schemas/router.d.ts.map +1 -0
- package/dist/schemas/router.js +26 -0
- package/dist/schemas/router.js.map +1 -0
- package/dist/schemas/server.d.ts +139 -0
- package/dist/schemas/server.d.ts.map +1 -0
- package/dist/schemas/server.js +124 -0
- package/dist/schemas/server.js.map +1 -0
- package/dist/schemas/world.d.ts +142 -0
- package/dist/schemas/world.d.ts.map +1 -0
- package/dist/schemas/world.js +124 -0
- package/dist/schemas/world.js.map +1 -0
- package/dist/utils/docker-compose.d.ts +23 -0
- package/dist/utils/docker-compose.d.ts.map +1 -0
- package/dist/utils/docker-compose.js +100 -0
- package/dist/utils/docker-compose.js.map +1 -0
- package/package.json +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# @minecraft-docker/mcctl-api
|
|
2
|
+
|
|
3
|
+
REST API server for managing Docker Minecraft servers.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Native Execution (Recommended)
|
|
8
|
+
|
|
9
|
+
The recommended way to run mcctl-api is natively with Node.js and PM2.
|
|
10
|
+
|
|
11
|
+
#### Prerequisites
|
|
12
|
+
|
|
13
|
+
- Node.js 20+
|
|
14
|
+
- pnpm (or npm/yarn)
|
|
15
|
+
- PM2 (for production deployment)
|
|
16
|
+
- Docker and Docker Compose (for managing Minecraft servers)
|
|
17
|
+
|
|
18
|
+
#### Setup
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# From the project root
|
|
22
|
+
pnpm install
|
|
23
|
+
pnpm --filter @minecraft-docker/mcctl-api build
|
|
24
|
+
|
|
25
|
+
# Start in development mode
|
|
26
|
+
cd platform/services/mcctl-api
|
|
27
|
+
pnpm dev
|
|
28
|
+
|
|
29
|
+
# Or start in production mode
|
|
30
|
+
pnpm start
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
#### PM2 Deployment
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Install PM2 globally
|
|
37
|
+
npm install -g pm2
|
|
38
|
+
|
|
39
|
+
# Start with PM2
|
|
40
|
+
cd platform/services/mcctl-api
|
|
41
|
+
pnpm start:pm2
|
|
42
|
+
|
|
43
|
+
# Other PM2 commands
|
|
44
|
+
pnpm stop:pm2 # Stop the service
|
|
45
|
+
pnpm restart:pm2 # Restart the service
|
|
46
|
+
pnpm logs:pm2 # View logs
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Docker (Legacy)
|
|
50
|
+
|
|
51
|
+
Docker deployment is deprecated but still supported for legacy installations.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# From project root
|
|
55
|
+
docker build -f platform/services/mcctl-api/docker/Dockerfile -t mcctl-api .
|
|
56
|
+
|
|
57
|
+
docker run -d \
|
|
58
|
+
--name mcctl-api \
|
|
59
|
+
-p 5001:5001 \
|
|
60
|
+
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
61
|
+
-v ~/minecraft-servers:/data \
|
|
62
|
+
-e MCCTL_ROOT=/data \
|
|
63
|
+
mcctl-api
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Configuration
|
|
67
|
+
|
|
68
|
+
### Environment Variables
|
|
69
|
+
|
|
70
|
+
| Variable | Description | Default |
|
|
71
|
+
|----------|-------------|---------|
|
|
72
|
+
| `PORT` | API server port | `5001` |
|
|
73
|
+
| `HOST` | Bind address | `0.0.0.0` |
|
|
74
|
+
| `NODE_ENV` | Environment (`development`, `production`, `test`) | `development` |
|
|
75
|
+
| `LOG_LEVEL` | Log level (`fatal`, `error`, `warn`, `info`, `debug`, `trace`) | `debug` (dev) / `info` (prod) |
|
|
76
|
+
| `MCCTL_ROOT` | Root directory for mcctl data | `~/minecraft-servers` |
|
|
77
|
+
| `PLATFORM_PATH` | Platform directory with docker-compose.yml | Same as `MCCTL_ROOT` |
|
|
78
|
+
| `AUTH_MODE` | Authentication mode | `disabled` (dev) / `api-key` (prod) |
|
|
79
|
+
| `AUTH_API_KEY` | API key for authentication | - |
|
|
80
|
+
| `AUTH_IP_WHITELIST` | Comma-separated IP whitelist | - |
|
|
81
|
+
| `SWAGGER_ENABLED` | Enable Swagger UI in production | `false` |
|
|
82
|
+
|
|
83
|
+
### Authentication Modes
|
|
84
|
+
|
|
85
|
+
- `disabled`: No authentication (development only)
|
|
86
|
+
- `api-key`: Require X-API-Key header
|
|
87
|
+
- `ip-whitelist`: Restrict access by IP address
|
|
88
|
+
- `basic`: HTTP Basic authentication
|
|
89
|
+
- `combined`: Require both API key and IP whitelist
|
|
90
|
+
|
|
91
|
+
### Example .env
|
|
92
|
+
|
|
93
|
+
```env
|
|
94
|
+
PORT=5001
|
|
95
|
+
HOST=0.0.0.0
|
|
96
|
+
NODE_ENV=production
|
|
97
|
+
LOG_LEVEL=info
|
|
98
|
+
MCCTL_ROOT=/home/user/minecraft-servers
|
|
99
|
+
AUTH_MODE=api-key
|
|
100
|
+
AUTH_API_KEY=your-secret-api-key
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## API Endpoints
|
|
104
|
+
|
|
105
|
+
- `GET /health` - Health check
|
|
106
|
+
- `GET /api/servers` - List all servers
|
|
107
|
+
- `GET /api/servers/:name` - Get server details
|
|
108
|
+
- `POST /api/servers/:name/start` - Start server
|
|
109
|
+
- `POST /api/servers/:name/stop` - Stop server
|
|
110
|
+
- `POST /api/servers/:name/restart` - Restart server
|
|
111
|
+
- `GET /api/servers/:name/logs` - Get server logs
|
|
112
|
+
- `POST /api/servers/:name/exec` - Execute RCON command
|
|
113
|
+
- `GET /api/worlds` - List all worlds
|
|
114
|
+
- `POST /api/console/exec` - Execute RCON command
|
|
115
|
+
|
|
116
|
+
## Graceful Shutdown
|
|
117
|
+
|
|
118
|
+
The API server handles graceful shutdown for:
|
|
119
|
+
- `SIGTERM` - Sent by PM2 and container orchestrators
|
|
120
|
+
- `SIGINT` - Sent by Ctrl+C
|
|
121
|
+
|
|
122
|
+
This ensures:
|
|
123
|
+
- Active requests are completed
|
|
124
|
+
- Connections are properly closed
|
|
125
|
+
- Resources are cleaned up
|
|
126
|
+
|
|
127
|
+
## Development
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Run tests
|
|
131
|
+
pnpm test
|
|
132
|
+
|
|
133
|
+
# Run tests in watch mode
|
|
134
|
+
pnpm test:watch
|
|
135
|
+
|
|
136
|
+
# Start development server with hot reload
|
|
137
|
+
pnpm dev
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Documentation
|
|
141
|
+
|
|
142
|
+
API documentation is available at `/docs` when running in development mode or when `SWAGGER_ENABLED=true`.
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAenD,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,eAAe,CAAC,CAgFtF;AAED,eAAe,QAAQ,CAAC"}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import Fastify from 'fastify';
|
|
2
|
+
import cors from '@fastify/cors';
|
|
3
|
+
import helmet from '@fastify/helmet';
|
|
4
|
+
import { config } from './config/index.js';
|
|
5
|
+
import authPlugin from './plugins/auth.js';
|
|
6
|
+
import swaggerPlugin from './plugins/swagger.js';
|
|
7
|
+
import serversRoutes from './routes/servers.js';
|
|
8
|
+
import serverActionsRoutes from './routes/servers/actions.js';
|
|
9
|
+
import consoleRoutes from './routes/console.js';
|
|
10
|
+
import worldsRoutes from './routes/worlds.js';
|
|
11
|
+
import authRoutes from './routes/auth.js';
|
|
12
|
+
import routerRoutes from './routes/router.js';
|
|
13
|
+
import playersRoutes from './routes/players.js';
|
|
14
|
+
import backupRoutes from './routes/backup.js';
|
|
15
|
+
export async function buildApp(options = {}) {
|
|
16
|
+
const app = Fastify({
|
|
17
|
+
logger: options.logger ?? config.nodeEnv !== 'test' ? {
|
|
18
|
+
level: config.logLevel,
|
|
19
|
+
} : false,
|
|
20
|
+
});
|
|
21
|
+
// Register CORS - allow all origins
|
|
22
|
+
await app.register(cors, {
|
|
23
|
+
origin: '*',
|
|
24
|
+
});
|
|
25
|
+
// Only enable Helmet in production to avoid HTTPS issues in development
|
|
26
|
+
if (config.nodeEnv === 'production') {
|
|
27
|
+
await app.register(helmet, {
|
|
28
|
+
contentSecurityPolicy: true,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
// Register authentication plugin
|
|
32
|
+
await app.register(authPlugin, {
|
|
33
|
+
config: config.auth,
|
|
34
|
+
});
|
|
35
|
+
// Register Swagger documentation (only in non-production or if explicitly enabled)
|
|
36
|
+
if (config.nodeEnv !== 'production' || process.env['SWAGGER_ENABLED'] === 'true') {
|
|
37
|
+
await app.register(swaggerPlugin, {
|
|
38
|
+
title: 'mcctl-api',
|
|
39
|
+
description: 'REST API for managing Docker Minecraft servers',
|
|
40
|
+
version: '0.1.0',
|
|
41
|
+
routePrefix: '/docs',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// Register server routes
|
|
45
|
+
await app.register(serversRoutes);
|
|
46
|
+
await app.register(serverActionsRoutes);
|
|
47
|
+
// Register router routes
|
|
48
|
+
await app.register(routerRoutes);
|
|
49
|
+
// Register player routes
|
|
50
|
+
await app.register(playersRoutes);
|
|
51
|
+
// Register backup routes
|
|
52
|
+
await app.register(backupRoutes);
|
|
53
|
+
// Register world routes
|
|
54
|
+
await app.register(worldsRoutes);
|
|
55
|
+
// Health check endpoint
|
|
56
|
+
app.get('/health', async () => {
|
|
57
|
+
return {
|
|
58
|
+
status: 'ok',
|
|
59
|
+
timestamp: new Date().toISOString(),
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
// Register API routes
|
|
63
|
+
await app.register(authRoutes);
|
|
64
|
+
await app.register(consoleRoutes);
|
|
65
|
+
// Graceful shutdown handler
|
|
66
|
+
const gracefulShutdown = async (signal) => {
|
|
67
|
+
app.log.info(`Received ${signal}, shutting down gracefully...`);
|
|
68
|
+
try {
|
|
69
|
+
await app.close();
|
|
70
|
+
app.log.info('Server closed successfully');
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
app.log.error(err, 'Error during shutdown');
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
79
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
80
|
+
return app;
|
|
81
|
+
}
|
|
82
|
+
export default buildApp;
|
|
83
|
+
//# sourceMappingURL=app.js.map
|
package/dist/app.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type AuthMode = 'disabled' | 'api-key' | 'ip-whitelist' | 'basic' | 'combined';
|
|
2
|
+
export interface AuthUser {
|
|
3
|
+
username: string;
|
|
4
|
+
passwordHash: string;
|
|
5
|
+
}
|
|
6
|
+
export interface AuthConfig {
|
|
7
|
+
mode: AuthMode;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
ipWhitelist?: string[];
|
|
10
|
+
users?: AuthUser[];
|
|
11
|
+
excludePaths?: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface AppConfig {
|
|
14
|
+
port: number;
|
|
15
|
+
host: string;
|
|
16
|
+
nodeEnv: 'development' | 'production' | 'test';
|
|
17
|
+
logLevel: 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace' | 'silent';
|
|
18
|
+
auth: AuthConfig;
|
|
19
|
+
/**
|
|
20
|
+
* Root directory for mcctl data.
|
|
21
|
+
* Default: MCCTL_ROOT env or ~/minecraft-servers
|
|
22
|
+
*/
|
|
23
|
+
mcctlRoot: string;
|
|
24
|
+
/**
|
|
25
|
+
* Platform directory containing docker-compose.yml and server configurations.
|
|
26
|
+
* Default: PLATFORM_PATH env or mcctlRoot
|
|
27
|
+
*/
|
|
28
|
+
platformPath: string;
|
|
29
|
+
}
|
|
30
|
+
export declare const config: AppConfig;
|
|
31
|
+
export default config;
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,cAAc,GAAG,OAAO,GAAG,UAAU,CAAC;AAEtF,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,aAAa,GAAG,YAAY,GAAG,MAAM,CAAC;IAC/C,QAAQ,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC7E,IAAI,EAAE,UAAU,CAAC;IACjB;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;CACtB;AA4HD,eAAO,MAAM,MAAM,EAAE,SAQpB,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { config as loadEnv } from 'dotenv';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
// Load .env file
|
|
5
|
+
loadEnv();
|
|
6
|
+
function getEnv(key, defaultValue) {
|
|
7
|
+
return process.env[key] ?? defaultValue;
|
|
8
|
+
}
|
|
9
|
+
function getEnvNumber(key, defaultValue) {
|
|
10
|
+
const value = process.env[key];
|
|
11
|
+
if (value === undefined) {
|
|
12
|
+
return defaultValue;
|
|
13
|
+
}
|
|
14
|
+
const parsed = parseInt(value, 10);
|
|
15
|
+
return isNaN(parsed) ? defaultValue : parsed;
|
|
16
|
+
}
|
|
17
|
+
function getNodeEnv() {
|
|
18
|
+
const env = process.env['NODE_ENV'];
|
|
19
|
+
if (env === 'production' || env === 'test') {
|
|
20
|
+
return env;
|
|
21
|
+
}
|
|
22
|
+
return 'development';
|
|
23
|
+
}
|
|
24
|
+
function getLogLevel() {
|
|
25
|
+
const level = process.env['LOG_LEVEL'];
|
|
26
|
+
const validLevels = ['fatal', 'error', 'warn', 'info', 'debug', 'trace', 'silent'];
|
|
27
|
+
if (level && validLevels.includes(level)) {
|
|
28
|
+
return level;
|
|
29
|
+
}
|
|
30
|
+
// Default based on NODE_ENV
|
|
31
|
+
return getNodeEnv() === 'production' ? 'info' : 'debug';
|
|
32
|
+
}
|
|
33
|
+
function getAuthMode() {
|
|
34
|
+
const mode = process.env['AUTH_MODE'];
|
|
35
|
+
const validModes = ['disabled', 'api-key', 'ip-whitelist', 'basic', 'combined'];
|
|
36
|
+
if (mode && validModes.includes(mode)) {
|
|
37
|
+
return mode;
|
|
38
|
+
}
|
|
39
|
+
// Default based on NODE_ENV
|
|
40
|
+
return getNodeEnv() === 'production' ? 'api-key' : 'disabled';
|
|
41
|
+
}
|
|
42
|
+
function getIpWhitelist() {
|
|
43
|
+
const whitelist = process.env['AUTH_IP_WHITELIST'];
|
|
44
|
+
if (!whitelist) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
// Split by comma and trim whitespace
|
|
48
|
+
return whitelist.split(',').map((ip) => ip.trim()).filter((ip) => ip.length > 0);
|
|
49
|
+
}
|
|
50
|
+
function getAuthUsers() {
|
|
51
|
+
const usersJson = process.env['AUTH_USERS'];
|
|
52
|
+
if (!usersJson) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const users = JSON.parse(usersJson);
|
|
57
|
+
if (!Array.isArray(users)) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
return users.filter((u) => typeof u === 'object' &&
|
|
61
|
+
u !== null &&
|
|
62
|
+
typeof u.username === 'string' &&
|
|
63
|
+
typeof u.passwordHash === 'string');
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function getExcludePaths() {
|
|
70
|
+
const paths = process.env['AUTH_EXCLUDE_PATHS'];
|
|
71
|
+
if (!paths) {
|
|
72
|
+
return ['/health', '/health/'];
|
|
73
|
+
}
|
|
74
|
+
return paths.split(',').map((p) => p.trim()).filter((p) => p.length > 0);
|
|
75
|
+
}
|
|
76
|
+
function getAuthConfig() {
|
|
77
|
+
return {
|
|
78
|
+
mode: getAuthMode(),
|
|
79
|
+
apiKey: process.env['AUTH_API_KEY'],
|
|
80
|
+
ipWhitelist: getIpWhitelist(),
|
|
81
|
+
users: getAuthUsers(),
|
|
82
|
+
excludePaths: getExcludePaths(),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get MCCTL_ROOT directory.
|
|
87
|
+
* Priority:
|
|
88
|
+
* 1. MCCTL_ROOT environment variable
|
|
89
|
+
* 2. ~/minecraft-servers (default for native execution)
|
|
90
|
+
*/
|
|
91
|
+
function getMcctlRoot() {
|
|
92
|
+
const envRoot = process.env['MCCTL_ROOT'];
|
|
93
|
+
if (envRoot) {
|
|
94
|
+
return path.resolve(envRoot);
|
|
95
|
+
}
|
|
96
|
+
// Default: ~/minecraft-servers (user's home directory)
|
|
97
|
+
return path.join(os.homedir(), 'minecraft-servers');
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get platform directory path.
|
|
101
|
+
* Priority:
|
|
102
|
+
* 1. PLATFORM_PATH environment variable
|
|
103
|
+
* 2. MCCTL_ROOT (same as mcctlRoot)
|
|
104
|
+
*/
|
|
105
|
+
function getPlatformPath(mcctlRoot) {
|
|
106
|
+
const envPath = process.env['PLATFORM_PATH'];
|
|
107
|
+
if (envPath) {
|
|
108
|
+
return path.resolve(envPath);
|
|
109
|
+
}
|
|
110
|
+
// Default: same as MCCTL_ROOT
|
|
111
|
+
return mcctlRoot;
|
|
112
|
+
}
|
|
113
|
+
const mcctlRoot = getMcctlRoot();
|
|
114
|
+
export const config = {
|
|
115
|
+
port: getEnvNumber('PORT', 5001),
|
|
116
|
+
host: getEnv('HOST', '0.0.0.0'),
|
|
117
|
+
nodeEnv: getNodeEnv(),
|
|
118
|
+
logLevel: getLogLevel(),
|
|
119
|
+
auth: getAuthConfig(),
|
|
120
|
+
mcctlRoot,
|
|
121
|
+
platformPath: getPlatformPath(mcctlRoot),
|
|
122
|
+
};
|
|
123
|
+
export default config;
|
|
124
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { buildApp } from './app.js';
|
|
3
|
+
import { config } from './config/index.js';
|
|
4
|
+
async function main() {
|
|
5
|
+
const app = await buildApp();
|
|
6
|
+
try {
|
|
7
|
+
await app.listen({
|
|
8
|
+
port: config.port,
|
|
9
|
+
host: config.host,
|
|
10
|
+
});
|
|
11
|
+
app.log.info(`Server listening on http://${config.host}:${config.port}`);
|
|
12
|
+
app.log.info(`Health check: http://${config.host}:${config.port}/health`);
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
app.log.error(err);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
main();
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,MAAM,GAAG,GAAG,MAAM,QAAQ,EAAE,CAAC;IAE7B,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,MAAM,CAAC;YACf,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI;SAClB,CAAC,CAAC;QAEH,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,8BAA8B,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACzE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,wBAAwB,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,SAAS,CAAC,CAAC;IAC5E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of an RCON command execution
|
|
3
|
+
*/
|
|
4
|
+
export interface RconResult {
|
|
5
|
+
exitCode: number;
|
|
6
|
+
output: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Get container name from server name
|
|
10
|
+
* Ensures the container name has the 'mc-' prefix
|
|
11
|
+
*/
|
|
12
|
+
export declare function getContainerName(serverName: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Execute RCON command and capture output
|
|
15
|
+
* Uses docker exec to run rcon-cli inside the container
|
|
16
|
+
*/
|
|
17
|
+
export declare function execRcon(containerName: string, command: string): Promise<RconResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Check if a Docker container is running
|
|
20
|
+
*/
|
|
21
|
+
export declare function isContainerRunning(containerName: string): Promise<boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* Execute RCON command by server name (convenience wrapper)
|
|
24
|
+
*/
|
|
25
|
+
export declare function execRconCommand(serverName: string, command: string): Promise<string>;
|
|
26
|
+
/**
|
|
27
|
+
* Parse player list from RCON response
|
|
28
|
+
* Handles: "There are X of a max of Y players online: player1, player2"
|
|
29
|
+
*/
|
|
30
|
+
export declare function parsePlayerList(response: string): {
|
|
31
|
+
online: number;
|
|
32
|
+
max: number;
|
|
33
|
+
players: string[];
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=rcon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rcon.d.ts","sourceRoot":"","sources":["../../src/lib/rcon.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,UAAU,CAAC,CAmCrB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBhF;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG1F;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAkBpG"}
|
package/dist/lib/rcon.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Get container name from server name
|
|
4
|
+
* Ensures the container name has the 'mc-' prefix
|
|
5
|
+
*/
|
|
6
|
+
export function getContainerName(serverName) {
|
|
7
|
+
return serverName.startsWith('mc-') ? serverName : `mc-${serverName}`;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Execute RCON command and capture output
|
|
11
|
+
* Uses docker exec to run rcon-cli inside the container
|
|
12
|
+
*/
|
|
13
|
+
export async function execRcon(containerName, command) {
|
|
14
|
+
// Split command into parts for rcon-cli
|
|
15
|
+
// Note: rcon-cli accepts the full command as a single argument
|
|
16
|
+
const normalizedContainer = getContainerName(containerName);
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
const child = spawn('docker', ['exec', normalizedContainer, 'rcon-cli', command], {
|
|
19
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
20
|
+
});
|
|
21
|
+
let stdout = '';
|
|
22
|
+
let stderr = '';
|
|
23
|
+
child.stdout.on('data', (data) => {
|
|
24
|
+
stdout += data.toString();
|
|
25
|
+
});
|
|
26
|
+
child.stderr.on('data', (data) => {
|
|
27
|
+
stderr += data.toString();
|
|
28
|
+
});
|
|
29
|
+
child.on('close', (code) => {
|
|
30
|
+
resolve({
|
|
31
|
+
exitCode: code ?? 1,
|
|
32
|
+
output: stdout || stderr,
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
child.on('error', (err) => {
|
|
36
|
+
resolve({
|
|
37
|
+
exitCode: 1,
|
|
38
|
+
output: err.message,
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check if a Docker container is running
|
|
45
|
+
*/
|
|
46
|
+
export async function isContainerRunning(containerName) {
|
|
47
|
+
const normalizedContainer = getContainerName(containerName);
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
const child = spawn('docker', ['inspect', '-f', '{{.State.Running}}', normalizedContainer], {
|
|
50
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
51
|
+
});
|
|
52
|
+
let output = '';
|
|
53
|
+
child.stdout.on('data', (data) => {
|
|
54
|
+
output += data.toString();
|
|
55
|
+
});
|
|
56
|
+
child.on('close', (code) => {
|
|
57
|
+
resolve(code === 0 && output.trim() === 'true');
|
|
58
|
+
});
|
|
59
|
+
child.on('error', () => {
|
|
60
|
+
resolve(false);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Execute RCON command by server name (convenience wrapper)
|
|
66
|
+
*/
|
|
67
|
+
export async function execRconCommand(serverName, command) {
|
|
68
|
+
const result = await execRcon(serverName, command);
|
|
69
|
+
return result.output.trim();
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Parse player list from RCON response
|
|
73
|
+
* Handles: "There are X of a max of Y players online: player1, player2"
|
|
74
|
+
*/
|
|
75
|
+
export function parsePlayerList(response) {
|
|
76
|
+
const match = response.match(/There are (\d+)(?:\s+of a max of\s+|\/)(\d+) players? online/i);
|
|
77
|
+
if (!match || !match[1] || !match[2]) {
|
|
78
|
+
return { online: 0, max: 20, players: [] };
|
|
79
|
+
}
|
|
80
|
+
const online = parseInt(match[1], 10);
|
|
81
|
+
const max = parseInt(match[2], 10);
|
|
82
|
+
// Extract player names after the colon
|
|
83
|
+
const playersMatch = response.match(/:\s*(.*)$/);
|
|
84
|
+
let players = [];
|
|
85
|
+
if (playersMatch && playersMatch[1]) {
|
|
86
|
+
players = playersMatch[1].split(',').map(s => s.trim()).filter(Boolean);
|
|
87
|
+
}
|
|
88
|
+
return { online, max, players };
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=rcon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rcon.js","sourceRoot":"","sources":["../../src/lib/rcon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { FastifyPluginAsync } from 'fastify';
|
|
2
|
+
export type AuthMode = 'disabled' | 'api-key' | 'ip-whitelist' | 'basic' | 'combined';
|
|
3
|
+
export interface AuthUser {
|
|
4
|
+
username: string;
|
|
5
|
+
passwordHash: string;
|
|
6
|
+
}
|
|
7
|
+
export interface AuthConfig {
|
|
8
|
+
mode: AuthMode;
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
ipWhitelist?: string[];
|
|
11
|
+
users?: AuthUser[];
|
|
12
|
+
excludePaths?: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface AuthPluginOptions {
|
|
15
|
+
config: AuthConfig;
|
|
16
|
+
}
|
|
17
|
+
declare const authPlugin: FastifyPluginAsync<AuthPluginOptions>;
|
|
18
|
+
declare class AuthError extends Error {
|
|
19
|
+
statusCode: number;
|
|
20
|
+
constructor(message: string, statusCode?: number);
|
|
21
|
+
}
|
|
22
|
+
export declare function hashPassword(password: string, saltRounds?: number): Promise<string>;
|
|
23
|
+
declare module 'fastify' {
|
|
24
|
+
interface FastifyInstance {
|
|
25
|
+
authConfig: AuthConfig;
|
|
26
|
+
isAuthenticated: boolean;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
declare const _default: FastifyPluginAsync<AuthPluginOptions>;
|
|
30
|
+
export default _default;
|
|
31
|
+
export { authPlugin, AuthError };
|
|
32
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/plugins/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,kBAAkB,EAAgC,MAAM,SAAS,CAAC;AAS5F,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,cAAc,GAAG,OAAO,GAAG,UAAU,CAAC;AAEtF,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,UAAU,CAAC;CACpB;AAMD,QAAA,MAAM,UAAU,EAAE,kBAAkB,CAAC,iBAAiB,CAgDrD,CAAC;AAMF,cAAM,SAAU,SAAQ,KAAK;IAC3B,UAAU,EAAE,MAAM,CAAC;gBAEP,OAAO,EAAE,MAAM,EAAE,UAAU,GAAE,MAAY;CAKtD;AA2KD,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAE7F;AAMD,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,eAAe;QACvB,UAAU,EAAE,UAAU,CAAC;QACvB,eAAe,EAAE,OAAO,CAAC;KAC1B;CACF;;AAMD,wBAGG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC"}
|