@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
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Routes
|
|
3
|
+
*
|
|
4
|
+
* Handles user authentication for the mcctl-console.
|
|
5
|
+
* Validates credentials against users.yaml file.
|
|
6
|
+
*/
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { YamlUserRepository, Username } from '@minecraft-docker/shared';
|
|
9
|
+
/**
|
|
10
|
+
* Register authentication routes
|
|
11
|
+
*/
|
|
12
|
+
export default async function authRoutes(app) {
|
|
13
|
+
/**
|
|
14
|
+
* POST /api/auth/login
|
|
15
|
+
*
|
|
16
|
+
* Authenticate user with username and password.
|
|
17
|
+
* Returns user info on success, 401 on failure.
|
|
18
|
+
*/
|
|
19
|
+
app.post('/api/auth/login', {
|
|
20
|
+
schema: {
|
|
21
|
+
description: 'Authenticate user with credentials',
|
|
22
|
+
tags: ['auth'],
|
|
23
|
+
body: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
required: ['username', 'password'],
|
|
26
|
+
properties: {
|
|
27
|
+
username: { type: 'string', description: 'Username' },
|
|
28
|
+
password: { type: 'string', description: 'Password' },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
response: {
|
|
32
|
+
200: {
|
|
33
|
+
description: 'Authentication successful',
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
id: { type: 'string' },
|
|
37
|
+
username: { type: 'string' },
|
|
38
|
+
role: { type: 'string' },
|
|
39
|
+
name: { type: 'string' },
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
401: {
|
|
43
|
+
description: 'Authentication failed',
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: {
|
|
46
|
+
error: { type: 'string' },
|
|
47
|
+
message: { type: 'string' },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
}, async (request, reply) => {
|
|
53
|
+
const { username, password } = request.body;
|
|
54
|
+
if (!username || !password) {
|
|
55
|
+
return reply.status(401).send({
|
|
56
|
+
error: 'Unauthorized',
|
|
57
|
+
message: 'Username and password are required',
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
// Get users.yaml path from MCCTL_ROOT
|
|
62
|
+
const mcctlRoot = process.env['MCCTL_ROOT'] || process.cwd();
|
|
63
|
+
const usersPath = join(mcctlRoot, 'users.yaml');
|
|
64
|
+
const userRepo = new YamlUserRepository(usersPath);
|
|
65
|
+
// Find user by username
|
|
66
|
+
const usernameVO = Username.create(username);
|
|
67
|
+
const user = await userRepo.findByUsername(usernameVO);
|
|
68
|
+
if (!user) {
|
|
69
|
+
app.log.warn({ username }, 'Login failed: user not found');
|
|
70
|
+
return reply.status(401).send({
|
|
71
|
+
error: 'Unauthorized',
|
|
72
|
+
message: 'Invalid username or password',
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
// Verify password
|
|
76
|
+
const isValid = await userRepo.verifyPassword(password, user.passwordHash);
|
|
77
|
+
if (!isValid) {
|
|
78
|
+
app.log.warn({ username }, 'Login failed: invalid password');
|
|
79
|
+
return reply.status(401).send({
|
|
80
|
+
error: 'Unauthorized',
|
|
81
|
+
message: 'Invalid username or password',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
app.log.info({ username, role: user.role }, 'Login successful');
|
|
85
|
+
// Return user info (without password hash)
|
|
86
|
+
return reply.send({
|
|
87
|
+
id: user.id,
|
|
88
|
+
username: user.username.value,
|
|
89
|
+
role: user.role,
|
|
90
|
+
name: user.username.value, // Use username as display name
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
app.log.error(error, 'Login error');
|
|
95
|
+
return reply.status(500).send({
|
|
96
|
+
error: 'Internal Server Error',
|
|
97
|
+
message: 'Authentication service error',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
/**
|
|
102
|
+
* GET /api/auth/me
|
|
103
|
+
*
|
|
104
|
+
* Get current user info (requires authentication via X-User header from BFF proxy).
|
|
105
|
+
*/
|
|
106
|
+
app.get('/api/auth/me', {
|
|
107
|
+
schema: {
|
|
108
|
+
description: 'Get current authenticated user info',
|
|
109
|
+
tags: ['auth'],
|
|
110
|
+
response: {
|
|
111
|
+
200: {
|
|
112
|
+
description: 'User info',
|
|
113
|
+
type: 'object',
|
|
114
|
+
properties: {
|
|
115
|
+
username: { type: 'string' },
|
|
116
|
+
role: { type: 'string' },
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
401: {
|
|
120
|
+
description: 'Not authenticated',
|
|
121
|
+
type: 'object',
|
|
122
|
+
properties: {
|
|
123
|
+
error: { type: 'string' },
|
|
124
|
+
message: { type: 'string' },
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
}, async (request, reply) => {
|
|
130
|
+
const username = request.headers['x-user'];
|
|
131
|
+
const role = request.headers['x-role'];
|
|
132
|
+
if (!username) {
|
|
133
|
+
return reply.status(401).send({
|
|
134
|
+
error: 'Unauthorized',
|
|
135
|
+
message: 'Not authenticated',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return reply.send({
|
|
139
|
+
username,
|
|
140
|
+
role: role || 'user',
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { FastifyPluginAsync } from 'fastify';
|
|
2
|
+
/**
|
|
3
|
+
* Backup management routes plugin
|
|
4
|
+
*/
|
|
5
|
+
declare const backupPlugin: FastifyPluginAsync;
|
|
6
|
+
declare const _default: FastifyPluginAsync;
|
|
7
|
+
export default _default;
|
|
8
|
+
export { backupPlugin };
|
|
9
|
+
//# sourceMappingURL=backup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../src/routes/backup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,kBAAkB,EAAgC,MAAM,SAAS,CAAC;AA+B5F;;GAEG;AACH,QAAA,MAAM,YAAY,EAAE,kBAsRnB,CAAC;;AAEF,wBAGG;AAEH,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import fp from 'fastify-plugin';
|
|
2
|
+
import { exec } from 'child_process';
|
|
3
|
+
import { promisify } from 'util';
|
|
4
|
+
import { existsSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { config } from '../config/index.js';
|
|
7
|
+
import { BackupStatusResponseSchema, BackupPushRequestSchema, BackupPushResponseSchema, BackupHistoryResponseSchema, BackupRestoreRequestSchema, BackupRestoreResponseSchema, ErrorResponseSchema, } from '../schemas/backup.js';
|
|
8
|
+
const execPromise = promisify(exec);
|
|
9
|
+
/**
|
|
10
|
+
* Backup management routes plugin
|
|
11
|
+
*/
|
|
12
|
+
const backupPlugin = async (fastify) => {
|
|
13
|
+
const platformPath = config.platformPath;
|
|
14
|
+
const worldsPath = join(platformPath, 'worlds');
|
|
15
|
+
// Helper to check if backup is configured
|
|
16
|
+
const isBackupConfigured = () => {
|
|
17
|
+
const repo = process.env['BACKUP_GITHUB_REPO'];
|
|
18
|
+
const token = process.env['BACKUP_GITHUB_TOKEN'];
|
|
19
|
+
if (!repo || !token) {
|
|
20
|
+
return { configured: false };
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
configured: true,
|
|
24
|
+
repository: repo,
|
|
25
|
+
branch: process.env['BACKUP_GITHUB_BRANCH'] || 'main',
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* GET /api/backup/status
|
|
30
|
+
* Get backup configuration status
|
|
31
|
+
*/
|
|
32
|
+
fastify.get('/api/backup/status', {
|
|
33
|
+
schema: {
|
|
34
|
+
description: 'Get backup configuration status',
|
|
35
|
+
tags: ['backup'],
|
|
36
|
+
response: {
|
|
37
|
+
200: BackupStatusResponseSchema,
|
|
38
|
+
500: ErrorResponseSchema,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
}, async (_request, reply) => {
|
|
42
|
+
try {
|
|
43
|
+
const status = isBackupConfigured();
|
|
44
|
+
// Get last backup date if configured
|
|
45
|
+
let lastBackup;
|
|
46
|
+
if (status.configured && existsSync(worldsPath)) {
|
|
47
|
+
try {
|
|
48
|
+
const { stdout } = await execPromise('git log -1 --format=%cI', {
|
|
49
|
+
cwd: worldsPath,
|
|
50
|
+
timeout: 5000,
|
|
51
|
+
});
|
|
52
|
+
lastBackup = stdout.trim() || undefined;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Git not initialized or no commits
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return reply.send({
|
|
59
|
+
configured: status.configured,
|
|
60
|
+
repository: status.repository,
|
|
61
|
+
branch: status.branch,
|
|
62
|
+
lastBackup,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
fastify.log.error(error, 'Failed to get backup status');
|
|
67
|
+
return reply.code(500).send({ error: 'InternalServerError', message: 'Failed to get backup status' });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
/**
|
|
71
|
+
* POST /api/backup/push
|
|
72
|
+
* Push backup to GitHub
|
|
73
|
+
*/
|
|
74
|
+
fastify.post('/api/backup/push', {
|
|
75
|
+
schema: {
|
|
76
|
+
description: 'Push worlds backup to GitHub',
|
|
77
|
+
tags: ['backup'],
|
|
78
|
+
body: BackupPushRequestSchema,
|
|
79
|
+
response: {
|
|
80
|
+
200: BackupPushResponseSchema,
|
|
81
|
+
400: ErrorResponseSchema,
|
|
82
|
+
500: ErrorResponseSchema,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
}, async (request, reply) => {
|
|
86
|
+
const { message } = request.body;
|
|
87
|
+
try {
|
|
88
|
+
const status = isBackupConfigured();
|
|
89
|
+
if (!status.configured) {
|
|
90
|
+
return reply.code(400).send({
|
|
91
|
+
error: 'BadRequest',
|
|
92
|
+
message: 'Backup not configured. Set BACKUP_GITHUB_REPO and BACKUP_GITHUB_TOKEN environment variables.',
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (!existsSync(worldsPath)) {
|
|
96
|
+
return reply.code(400).send({
|
|
97
|
+
error: 'BadRequest',
|
|
98
|
+
message: 'Worlds directory not found',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
// Run backup script or git commands
|
|
102
|
+
const scriptPath = join(platformPath, 'scripts', 'backup.sh');
|
|
103
|
+
let command;
|
|
104
|
+
if (existsSync(scriptPath)) {
|
|
105
|
+
command = `bash "${scriptPath}" push ${message ? `--message "${message}"` : ''}`;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// Direct git commands
|
|
109
|
+
const commitMessage = message || `Backup ${new Date().toISOString()}`;
|
|
110
|
+
command = `cd "${worldsPath}" && git add -A && git commit -m "${commitMessage}" && git push`;
|
|
111
|
+
}
|
|
112
|
+
const { stdout } = await execPromise(command, {
|
|
113
|
+
cwd: platformPath,
|
|
114
|
+
timeout: 120000, // 2 minute timeout
|
|
115
|
+
env: {
|
|
116
|
+
...process.env,
|
|
117
|
+
MCCTL_ROOT: platformPath,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
// Get commit hash
|
|
121
|
+
let commitHash;
|
|
122
|
+
try {
|
|
123
|
+
const { stdout: hashOut } = await execPromise('git rev-parse --short HEAD', { cwd: worldsPath });
|
|
124
|
+
commitHash = hashOut.trim();
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Ignore
|
|
128
|
+
}
|
|
129
|
+
return reply.send({
|
|
130
|
+
success: true,
|
|
131
|
+
commitHash,
|
|
132
|
+
message: 'Backup pushed successfully',
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
const execError = error;
|
|
137
|
+
fastify.log.error(error, 'Failed to push backup');
|
|
138
|
+
// Check for "nothing to commit" which is not an error
|
|
139
|
+
if (execError.stderr?.includes('nothing to commit') || execError.message?.includes('nothing to commit')) {
|
|
140
|
+
return reply.send({
|
|
141
|
+
success: true,
|
|
142
|
+
message: 'No changes to backup',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return reply.code(500).send({
|
|
146
|
+
error: 'InternalServerError',
|
|
147
|
+
message: execError.stderr || execError.message || 'Failed to push backup',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
/**
|
|
152
|
+
* GET /api/backup/history
|
|
153
|
+
* Get backup history (git commits)
|
|
154
|
+
*/
|
|
155
|
+
fastify.get('/api/backup/history', {
|
|
156
|
+
schema: {
|
|
157
|
+
description: 'Get backup history (git commits)',
|
|
158
|
+
tags: ['backup'],
|
|
159
|
+
response: {
|
|
160
|
+
200: BackupHistoryResponseSchema,
|
|
161
|
+
400: ErrorResponseSchema,
|
|
162
|
+
500: ErrorResponseSchema,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
}, async (_request, reply) => {
|
|
166
|
+
try {
|
|
167
|
+
const status = isBackupConfigured();
|
|
168
|
+
if (!status.configured) {
|
|
169
|
+
return reply.code(400).send({
|
|
170
|
+
error: 'BadRequest',
|
|
171
|
+
message: 'Backup not configured',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
if (!existsSync(worldsPath)) {
|
|
175
|
+
return reply.send({ commits: [], total: 0 });
|
|
176
|
+
}
|
|
177
|
+
// Get git log
|
|
178
|
+
const { stdout } = await execPromise('git log --format="%H|%s|%cI|%an" -n 20', { cwd: worldsPath, timeout: 10000 });
|
|
179
|
+
const commits = stdout.trim().split('\n')
|
|
180
|
+
.filter(Boolean)
|
|
181
|
+
.map(line => {
|
|
182
|
+
const [hash, message, date, author] = line.split('|');
|
|
183
|
+
return {
|
|
184
|
+
hash: hash?.substring(0, 7) || '',
|
|
185
|
+
message: message || '',
|
|
186
|
+
date: date || '',
|
|
187
|
+
author,
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
return reply.send({ commits, total: commits.length });
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
const execError = error;
|
|
194
|
+
// Check for "not a git repository"
|
|
195
|
+
if (execError.stderr?.includes('not a git repository')) {
|
|
196
|
+
return reply.send({ commits: [], total: 0 });
|
|
197
|
+
}
|
|
198
|
+
fastify.log.error(error, 'Failed to get backup history');
|
|
199
|
+
return reply.code(500).send({ error: 'InternalServerError', message: 'Failed to get backup history' });
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
/**
|
|
203
|
+
* POST /api/backup/restore
|
|
204
|
+
* Restore from backup
|
|
205
|
+
*/
|
|
206
|
+
fastify.post('/api/backup/restore', {
|
|
207
|
+
schema: {
|
|
208
|
+
description: 'Restore worlds from a backup commit',
|
|
209
|
+
tags: ['backup'],
|
|
210
|
+
body: BackupRestoreRequestSchema,
|
|
211
|
+
response: {
|
|
212
|
+
200: BackupRestoreResponseSchema,
|
|
213
|
+
400: ErrorResponseSchema,
|
|
214
|
+
500: ErrorResponseSchema,
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
}, async (request, reply) => {
|
|
218
|
+
const { commitHash } = request.body;
|
|
219
|
+
try {
|
|
220
|
+
const status = isBackupConfigured();
|
|
221
|
+
if (!status.configured) {
|
|
222
|
+
return reply.code(400).send({
|
|
223
|
+
error: 'BadRequest',
|
|
224
|
+
message: 'Backup not configured',
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
if (!existsSync(worldsPath)) {
|
|
228
|
+
return reply.code(400).send({
|
|
229
|
+
error: 'BadRequest',
|
|
230
|
+
message: 'Worlds directory not found',
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
// Run backup restore script or git commands
|
|
234
|
+
const scriptPath = join(platformPath, 'scripts', 'backup.sh');
|
|
235
|
+
let command;
|
|
236
|
+
if (existsSync(scriptPath)) {
|
|
237
|
+
command = `bash "${scriptPath}" restore "${commitHash}"`;
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
// Direct git command
|
|
241
|
+
command = `cd "${worldsPath}" && git checkout ${commitHash} .`;
|
|
242
|
+
}
|
|
243
|
+
await execPromise(command, {
|
|
244
|
+
cwd: platformPath,
|
|
245
|
+
timeout: 120000,
|
|
246
|
+
env: {
|
|
247
|
+
...process.env,
|
|
248
|
+
MCCTL_ROOT: platformPath,
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
return reply.send({
|
|
252
|
+
success: true,
|
|
253
|
+
message: `Restored to commit ${commitHash}`,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
const execError = error;
|
|
258
|
+
fastify.log.error(error, 'Failed to restore backup');
|
|
259
|
+
return reply.code(500).send({
|
|
260
|
+
error: 'InternalServerError',
|
|
261
|
+
message: execError.stderr || execError.message || 'Failed to restore backup',
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
};
|
|
266
|
+
export default fp(backupPlugin, {
|
|
267
|
+
name: 'backup-routes',
|
|
268
|
+
fastify: '5.x',
|
|
269
|
+
});
|
|
270
|
+
export { backupPlugin };
|
|
271
|
+
//# sourceMappingURL=backup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/routes/backup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../src/routes/console.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,kBAAkB,EAAE,MAAM,SAAS,CAAC;AA0E9D,QAAA,MAAM,aAAa,EAAE,kBA6CpB,CAAC;;AAMF,wBAGG;AAEH,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import fp from 'fastify-plugin';
|
|
2
|
+
import { execRcon } from '../lib/rcon.js';
|
|
3
|
+
// ============================================================
|
|
4
|
+
// JSON Schema for validation
|
|
5
|
+
// ============================================================
|
|
6
|
+
const execCommandSchema = {
|
|
7
|
+
body: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
required: ['command'],
|
|
10
|
+
properties: {
|
|
11
|
+
command: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
minLength: 1,
|
|
14
|
+
description: 'RCON command to execute',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
params: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
required: ['id'],
|
|
21
|
+
properties: {
|
|
22
|
+
id: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Server ID (name)',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
response: {
|
|
29
|
+
200: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
output: { type: 'string' },
|
|
33
|
+
exitCode: { type: 'number' },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
400: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
error: { type: 'string' },
|
|
40
|
+
message: { type: 'string' },
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
// ============================================================
|
|
46
|
+
// Plugin Implementation
|
|
47
|
+
// ============================================================
|
|
48
|
+
const consoleRoutes = async (fastify) => {
|
|
49
|
+
/**
|
|
50
|
+
* POST /servers/:id/console/exec
|
|
51
|
+
* Execute an RCON command on the specified server
|
|
52
|
+
*/
|
|
53
|
+
fastify.post('/servers/:id/console/exec', {
|
|
54
|
+
schema: execCommandSchema,
|
|
55
|
+
}, async (request, reply) => {
|
|
56
|
+
const { id: serverId } = request.params;
|
|
57
|
+
const { command } = request.body;
|
|
58
|
+
// Validation is handled by schema, but double-check for safety
|
|
59
|
+
if (!command || command.trim().length === 0) {
|
|
60
|
+
return reply.code(400).send({
|
|
61
|
+
error: 'ValidationError',
|
|
62
|
+
message: 'Command is required and cannot be empty',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
fastify.log.info({ serverId, command }, 'Executing RCON command');
|
|
66
|
+
try {
|
|
67
|
+
const result = await execRcon(serverId, command);
|
|
68
|
+
return reply.send({
|
|
69
|
+
output: result.output.trim(),
|
|
70
|
+
exitCode: result.exitCode,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
fastify.log.error({ serverId, command, error }, 'RCON execution failed');
|
|
75
|
+
return reply.code(500).send({
|
|
76
|
+
error: 'RconExecutionError',
|
|
77
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
// ============================================================
|
|
83
|
+
// Export
|
|
84
|
+
// ============================================================
|
|
85
|
+
export default fp(consoleRoutes, {
|
|
86
|
+
name: 'console-routes',
|
|
87
|
+
fastify: '5.x',
|
|
88
|
+
});
|
|
89
|
+
export { consoleRoutes };
|
|
90
|
+
//# sourceMappingURL=console.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console.js","sourceRoot":"","sources":["../../src/routes/console.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { FastifyPluginAsync } from 'fastify';
|
|
2
|
+
/**
|
|
3
|
+
* Player management routes plugin
|
|
4
|
+
*/
|
|
5
|
+
declare const playersPlugin: FastifyPluginAsync;
|
|
6
|
+
declare const _default: FastifyPluginAsync;
|
|
7
|
+
export default _default;
|
|
8
|
+
export { playersPlugin };
|
|
9
|
+
//# sourceMappingURL=players.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"players.d.ts","sourceRoot":"","sources":["../../src/routes/players.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,kBAAkB,EAAgC,MAAM,SAAS,CAAC;AA4C5F;;GAEG;AACH,QAAA,MAAM,aAAa,EAAE,kBA6VpB,CAAC;;AAEF,wBAGG;AAEH,OAAO,EAAE,aAAa,EAAE,CAAC"}
|