@minecraft-docker/mcctl 1.5.0 → 1.6.1
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/dist/application/index.d.ts +2 -0
- package/dist/application/index.d.ts.map +1 -0
- package/dist/application/index.js +5 -0
- package/dist/application/index.js.map +1 -0
- package/dist/commands/backup.d.ts +16 -0
- package/dist/commands/backup.d.ts.map +1 -0
- package/dist/commands/backup.js +182 -0
- package/dist/commands/backup.js.map +1 -0
- package/dist/commands/ban.d.ts +14 -0
- package/dist/commands/ban.d.ts.map +1 -0
- package/dist/commands/ban.js +418 -0
- package/dist/commands/ban.js.map +1 -0
- package/dist/commands/config.d.ts +18 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +106 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/create.d.ts +22 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +83 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/delete.d.ts +17 -0
- package/dist/commands/delete.d.ts.map +1 -0
- package/dist/commands/delete.js +71 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/exec.d.ts +10 -0
- package/dist/commands/exec.d.ts.map +1 -0
- package/dist/commands/exec.js +29 -0
- package/dist/commands/exec.js.map +1 -0
- package/dist/commands/index.d.ts +18 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +18 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +186 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/kick.d.ts +12 -0
- package/dist/commands/kick.d.ts.map +1 -0
- package/dist/commands/kick.js +71 -0
- package/dist/commands/kick.js.map +1 -0
- package/dist/commands/migrate.d.ts +18 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +470 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/op.d.ts +12 -0
- package/dist/commands/op.d.ts.map +1 -0
- package/dist/commands/op.js +220 -0
- package/dist/commands/op.js.map +1 -0
- package/dist/commands/player-online.d.ts +11 -0
- package/dist/commands/player-online.d.ts.map +1 -0
- package/dist/commands/player-online.js +151 -0
- package/dist/commands/player-online.js.map +1 -0
- package/dist/commands/player.d.ts +18 -0
- package/dist/commands/player.d.ts.map +1 -0
- package/dist/commands/player.js +352 -0
- package/dist/commands/player.js.map +1 -0
- package/dist/commands/server-backup.d.ts +36 -0
- package/dist/commands/server-backup.d.ts.map +1 -0
- package/dist/commands/server-backup.js +320 -0
- package/dist/commands/server-backup.js.map +1 -0
- package/dist/commands/server-restore.d.ts +13 -0
- package/dist/commands/server-restore.d.ts.map +1 -0
- package/dist/commands/server-restore.js +294 -0
- package/dist/commands/server-restore.js.map +1 -0
- package/dist/commands/status.d.ts +13 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +325 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/whitelist.d.ts +12 -0
- package/dist/commands/whitelist.d.ts.map +1 -0
- package/dist/commands/whitelist.js +316 -0
- package/dist/commands/whitelist.js.map +1 -0
- package/dist/commands/world.d.ts +16 -0
- package/dist/commands/world.d.ts.map +1 -0
- package/dist/commands/world.js +135 -0
- package/dist/commands/world.js.map +1 -0
- package/dist/domain/index.d.ts +2 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +7 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +609 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/adapters/ClackPromptAdapter.d.ts +29 -0
- package/dist/infrastructure/adapters/ClackPromptAdapter.d.ts.map +1 -0
- package/dist/infrastructure/adapters/ClackPromptAdapter.js +419 -0
- package/dist/infrastructure/adapters/ClackPromptAdapter.js.map +1 -0
- package/dist/infrastructure/adapters/index.d.ts +3 -0
- package/dist/infrastructure/adapters/index.d.ts.map +1 -0
- package/dist/infrastructure/adapters/index.js +5 -0
- package/dist/infrastructure/adapters/index.js.map +1 -0
- package/dist/infrastructure/di/container.d.ts +47 -0
- package/dist/infrastructure/di/container.d.ts.map +1 -0
- package/dist/infrastructure/di/container.js +140 -0
- package/dist/infrastructure/di/container.js.map +1 -0
- package/dist/infrastructure/di/index.d.ts +2 -0
- package/dist/infrastructure/di/index.d.ts.map +1 -0
- package/dist/infrastructure/di/index.js +2 -0
- package/dist/infrastructure/di/index.js.map +1 -0
- package/dist/infrastructure/index.d.ts +4 -0
- package/dist/infrastructure/index.d.ts.map +1 -0
- package/dist/infrastructure/index.js +7 -0
- package/dist/infrastructure/index.js.map +1 -0
- package/dist/lib/mojang-api.d.ts +51 -0
- package/dist/lib/mojang-api.d.ts.map +1 -0
- package/dist/lib/mojang-api.js +136 -0
- package/dist/lib/mojang-api.js.map +1 -0
- package/dist/lib/player-cache.d.ts +79 -0
- package/dist/lib/player-cache.d.ts.map +1 -0
- package/dist/lib/player-cache.js +302 -0
- package/dist/lib/player-cache.js.map +1 -0
- package/dist/lib/player-json.d.ts +69 -0
- package/dist/lib/player-json.d.ts.map +1 -0
- package/dist/lib/player-json.js +96 -0
- package/dist/lib/player-json.js.map +1 -0
- package/dist/lib/prompts/action-select.d.ts +23 -0
- package/dist/lib/prompts/action-select.d.ts.map +1 -0
- package/dist/lib/prompts/action-select.js +124 -0
- package/dist/lib/prompts/action-select.js.map +1 -0
- package/dist/lib/prompts/index.d.ts +7 -0
- package/dist/lib/prompts/index.d.ts.map +1 -0
- package/dist/lib/prompts/index.js +7 -0
- package/dist/lib/prompts/index.js.map +1 -0
- package/dist/lib/prompts/player-select.d.ts +25 -0
- package/dist/lib/prompts/player-select.d.ts.map +1 -0
- package/dist/lib/prompts/player-select.js +122 -0
- package/dist/lib/prompts/player-select.js.map +1 -0
- package/dist/lib/prompts/server-select.d.ts +26 -0
- package/dist/lib/prompts/server-select.d.ts.map +1 -0
- package/dist/lib/prompts/server-select.js +99 -0
- package/dist/lib/prompts/server-select.js.map +1 -0
- package/dist/lib/rcon.d.ts +53 -0
- package/dist/lib/rcon.d.ts.map +1 -0
- package/dist/lib/rcon.js +131 -0
- package/dist/lib/rcon.js.map +1 -0
- package/dist/lib/shell.d.ts +109 -0
- package/dist/lib/shell.d.ts.map +1 -0
- package/dist/lib/shell.js +318 -0
- package/dist/lib/shell.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { type ICreateServerUseCase, type CreateServerConfig, type IDeleteServerUseCase, type IServerStatusUseCase, type IWorldManagementUseCase, type IBackupUseCase, type IPlayerLookupUseCase, type IPromptPort, type IShellPort, type CreateServerOptions, type LogsOptions, type ShellResult, type IServerRepository, type ServerConfigData, type IWorldRepository, type WorldLockData, type IDocProvider, type DocServerTypeInfo, type DocEnvVarInfo, type DocVersionInfo, type DocMemoryInfo, CreateServerUseCase, DeleteServerUseCase, ServerStatusUseCase, WorldManagementUseCase, BackupUseCase, PlayerLookupUseCase, } from '@minecraft-docker/shared';
|
|
2
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/application/index.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACnB,KAAK,oBAAoB,EAEzB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,aAAa,EAElB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,aAAa,EACb,mBAAmB,GACpB,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Re-export from shared package for backward compatibility
|
|
2
|
+
export {
|
|
3
|
+
// Use Cases
|
|
4
|
+
CreateServerUseCase, DeleteServerUseCase, ServerStatusUseCase, WorldManagementUseCase, BackupUseCase, PlayerLookupUseCase, } from '@minecraft-docker/shared';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/application/index.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,OAAO;AAwBL,YAAY;AACZ,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,aAAa,EACb,mBAAmB,GACpB,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backup command options
|
|
3
|
+
*/
|
|
4
|
+
export interface BackupCommandOptions {
|
|
5
|
+
root?: string;
|
|
6
|
+
subCommand?: string;
|
|
7
|
+
message?: string;
|
|
8
|
+
commitHash?: string;
|
|
9
|
+
json?: boolean;
|
|
10
|
+
auto?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Execute backup command
|
|
14
|
+
*/
|
|
15
|
+
export declare function backupCommand(options: BackupCommandOptions): Promise<number>;
|
|
16
|
+
//# sourceMappingURL=backup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../src/commands/backup.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8BlF"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { Paths, log, colors } from '@minecraft-docker/shared';
|
|
2
|
+
import { getContainer } from '../infrastructure/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Execute backup command
|
|
5
|
+
*/
|
|
6
|
+
export async function backupCommand(options) {
|
|
7
|
+
const paths = new Paths(options.root);
|
|
8
|
+
// Check if initialized
|
|
9
|
+
if (!paths.isInitialized()) {
|
|
10
|
+
log.error('Platform not initialized. Run: mcctl init');
|
|
11
|
+
return 1;
|
|
12
|
+
}
|
|
13
|
+
const container = getContainer(options.root);
|
|
14
|
+
const subCommand = options.subCommand ?? 'status';
|
|
15
|
+
switch (subCommand) {
|
|
16
|
+
case 'status':
|
|
17
|
+
return backupStatus(container, options);
|
|
18
|
+
case 'push':
|
|
19
|
+
return backupPush(container, options);
|
|
20
|
+
case 'history':
|
|
21
|
+
return backupHistory(container, options);
|
|
22
|
+
case 'restore':
|
|
23
|
+
return backupRestore(container, options);
|
|
24
|
+
default:
|
|
25
|
+
log.error(`Unknown backup subcommand: ${subCommand}`);
|
|
26
|
+
console.log('Usage: mcctl backup [status|push|history|restore]');
|
|
27
|
+
return 1;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Show backup status
|
|
32
|
+
*/
|
|
33
|
+
async function backupStatus(container, options) {
|
|
34
|
+
const useCase = container.backupUseCase;
|
|
35
|
+
try {
|
|
36
|
+
const status = await useCase.status();
|
|
37
|
+
if (options.json) {
|
|
38
|
+
console.log(JSON.stringify(status, null, 2));
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
console.log('');
|
|
42
|
+
console.log(colors.bold('Backup Configuration:'));
|
|
43
|
+
console.log('');
|
|
44
|
+
if (!status.configured) {
|
|
45
|
+
console.log(colors.yellow(' Status: Not configured'));
|
|
46
|
+
console.log('');
|
|
47
|
+
console.log(' To enable backups, set in .env:');
|
|
48
|
+
console.log(colors.dim(' BACKUP_GITHUB_TOKEN=your-token'));
|
|
49
|
+
console.log(colors.dim(' BACKUP_GITHUB_REPO=username/repo'));
|
|
50
|
+
console.log('');
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
console.log(colors.green(' Status: Configured'));
|
|
54
|
+
if (status.repository) {
|
|
55
|
+
console.log(` Repository: ${colors.cyan(status.repository)}`);
|
|
56
|
+
}
|
|
57
|
+
if (status.branch) {
|
|
58
|
+
console.log(` Branch: ${status.branch}`);
|
|
59
|
+
}
|
|
60
|
+
if (status.lastBackup) {
|
|
61
|
+
console.log(` Last backup: ${status.lastBackup.toLocaleString()}`);
|
|
62
|
+
}
|
|
63
|
+
if (status.autoBackupEnabled !== undefined) {
|
|
64
|
+
console.log(` Auto backup: ${status.autoBackupEnabled ? colors.green('enabled') : colors.dim('disabled')}`);
|
|
65
|
+
}
|
|
66
|
+
console.log('');
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71
|
+
log.error(message);
|
|
72
|
+
return 1;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Push backup to GitHub
|
|
77
|
+
*/
|
|
78
|
+
async function backupPush(container, options) {
|
|
79
|
+
const useCase = container.backupUseCase;
|
|
80
|
+
try {
|
|
81
|
+
// If message provided, use direct push
|
|
82
|
+
if (options.message) {
|
|
83
|
+
const result = await useCase.pushWithMessage(options.message);
|
|
84
|
+
if (result.success) {
|
|
85
|
+
console.log('');
|
|
86
|
+
console.log(colors.green('✓ Backup complete!'));
|
|
87
|
+
if (result.commitHash) {
|
|
88
|
+
console.log(` Commit: ${colors.cyan(result.commitHash)}`);
|
|
89
|
+
}
|
|
90
|
+
console.log('');
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
log.error(result.error || 'Backup failed');
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Interactive mode
|
|
99
|
+
const result = await useCase.push();
|
|
100
|
+
return result.success ? 0 : 1;
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
const prompt = container.promptPort;
|
|
104
|
+
if (prompt.isCancel(error)) {
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
108
|
+
log.error(message);
|
|
109
|
+
return 1;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Show backup history
|
|
114
|
+
*/
|
|
115
|
+
async function backupHistory(container, options) {
|
|
116
|
+
const useCase = container.backupUseCase;
|
|
117
|
+
try {
|
|
118
|
+
const history = await useCase.history();
|
|
119
|
+
if (history.length === 0) {
|
|
120
|
+
console.log('No backup history found');
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
if (options.json) {
|
|
124
|
+
console.log(JSON.stringify(history, null, 2));
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
console.log('');
|
|
128
|
+
console.log(colors.bold('Backup History:'));
|
|
129
|
+
console.log('');
|
|
130
|
+
for (const entry of history.slice(0, 10)) {
|
|
131
|
+
const hash = colors.cyan(entry.commitHash.substring(0, 7));
|
|
132
|
+
const date = colors.dim(entry.date.toLocaleDateString());
|
|
133
|
+
console.log(` ${hash} ${entry.message} ${date}`);
|
|
134
|
+
}
|
|
135
|
+
if (history.length > 10) {
|
|
136
|
+
console.log(colors.dim(` ... and ${history.length - 10} more`));
|
|
137
|
+
}
|
|
138
|
+
console.log('');
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
143
|
+
log.error(message);
|
|
144
|
+
return 1;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Restore from backup
|
|
149
|
+
*/
|
|
150
|
+
async function backupRestore(container, options) {
|
|
151
|
+
const useCase = container.backupUseCase;
|
|
152
|
+
try {
|
|
153
|
+
// If commit hash provided, use direct restore
|
|
154
|
+
if (options.commitHash) {
|
|
155
|
+
const result = await useCase.restoreFromCommit(options.commitHash);
|
|
156
|
+
if (result.success) {
|
|
157
|
+
console.log('');
|
|
158
|
+
console.log(colors.green('✓ Restore complete!'));
|
|
159
|
+
console.log(` Restored from: ${colors.cyan(result.commitHash.substring(0, 7))}`);
|
|
160
|
+
console.log('');
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
log.error(result.error || 'Restore failed');
|
|
165
|
+
return 1;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Interactive mode
|
|
169
|
+
const result = await useCase.restore();
|
|
170
|
+
return result.success ? 0 : 1;
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
const prompt = container.promptPort;
|
|
174
|
+
if (prompt.isCancel(error)) {
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
178
|
+
log.error(message);
|
|
179
|
+
return 1;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=backup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/commands/backup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface BanCommandOptions {
|
|
2
|
+
root?: string;
|
|
3
|
+
serverName?: string;
|
|
4
|
+
subCommand?: 'list' | 'add' | 'remove' | 'ip';
|
|
5
|
+
target?: string;
|
|
6
|
+
reason?: string;
|
|
7
|
+
ipAction?: 'list' | 'add' | 'remove';
|
|
8
|
+
json?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Manage server bans
|
|
12
|
+
*/
|
|
13
|
+
export declare function banCommand(options: BanCommandOptions): Promise<number>;
|
|
14
|
+
//# sourceMappingURL=ban.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ban.d.ts","sourceRoot":"","sources":["../../src/commands/ban.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IACrC,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CA6D5E"}
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { Paths, log, colors } from '@minecraft-docker/shared';
|
|
3
|
+
import { ShellExecutor } from '../lib/shell.js';
|
|
4
|
+
import { isContainerRunning, execRconWithOutput, getContainerName } from '../lib/rcon.js';
|
|
5
|
+
import { readBannedPlayersJson, writeBannedPlayersJson, readBannedIpsJson, writeBannedIpsJson, findBannedPlayerByName, findBannedIp, createTimestamp, } from '../lib/player-json.js';
|
|
6
|
+
/**
|
|
7
|
+
* Manage server bans
|
|
8
|
+
*/
|
|
9
|
+
export async function banCommand(options) {
|
|
10
|
+
const paths = new Paths(options.root);
|
|
11
|
+
if (!paths.isInitialized()) {
|
|
12
|
+
log.error('Platform not initialized. Run: mcctl init');
|
|
13
|
+
return 1;
|
|
14
|
+
}
|
|
15
|
+
if (!options.serverName) {
|
|
16
|
+
log.error('Server name is required');
|
|
17
|
+
log.info('Usage: mcctl ban <server> <list|add|remove|ip> [player/ip] [reason]');
|
|
18
|
+
return 1;
|
|
19
|
+
}
|
|
20
|
+
if (!options.subCommand) {
|
|
21
|
+
log.error('Action is required: list, add, remove, ip');
|
|
22
|
+
log.info('Usage: mcctl ban <server> <list|add|remove|ip> [player/ip] [reason]');
|
|
23
|
+
return 1;
|
|
24
|
+
}
|
|
25
|
+
const shell = new ShellExecutor(paths);
|
|
26
|
+
const config = shell.readConfig(options.serverName);
|
|
27
|
+
if (config === null) {
|
|
28
|
+
log.error(`Server '${options.serverName}' not found`);
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
const containerName = getContainerName(options.serverName);
|
|
32
|
+
const isRunning = await isContainerRunning(containerName);
|
|
33
|
+
const bannedPlayersPath = join(paths.servers, options.serverName, 'data', 'banned-players.json');
|
|
34
|
+
const bannedIpsPath = join(paths.servers, options.serverName, 'data', 'banned-ips.json');
|
|
35
|
+
switch (options.subCommand) {
|
|
36
|
+
case 'list':
|
|
37
|
+
return await listBannedPlayers(options, bannedPlayersPath, containerName, isRunning);
|
|
38
|
+
case 'add':
|
|
39
|
+
if (!options.target) {
|
|
40
|
+
log.error('Player name is required for ban');
|
|
41
|
+
log.info('Usage: mcctl ban <server> add <player> [reason]');
|
|
42
|
+
return 1;
|
|
43
|
+
}
|
|
44
|
+
return await banPlayer(options, bannedPlayersPath, containerName, isRunning);
|
|
45
|
+
case 'remove':
|
|
46
|
+
if (!options.target) {
|
|
47
|
+
log.error('Player name is required for unban');
|
|
48
|
+
log.info('Usage: mcctl ban <server> remove <player>');
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
return await unbanPlayer(options, bannedPlayersPath, containerName, isRunning);
|
|
52
|
+
case 'ip':
|
|
53
|
+
return await handleIpBan(options, bannedIpsPath, containerName, isRunning);
|
|
54
|
+
default:
|
|
55
|
+
log.error(`Unknown action: ${options.subCommand}`);
|
|
56
|
+
log.info('Valid actions: list, add, remove, ip');
|
|
57
|
+
return 1;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* List banned players
|
|
62
|
+
*/
|
|
63
|
+
async function listBannedPlayers(options, bannedPlayersPath, containerName, isRunning) {
|
|
64
|
+
let players = [];
|
|
65
|
+
let source = 'json';
|
|
66
|
+
if (isRunning) {
|
|
67
|
+
const result = await execRconWithOutput(containerName, ['banlist', 'players']);
|
|
68
|
+
if (result.code === 0) {
|
|
69
|
+
// Parse output - format varies by MC version
|
|
70
|
+
const match = result.output.match(/:\s*(.+)$/);
|
|
71
|
+
if (match && match[1] && !match[1].includes('There are no')) {
|
|
72
|
+
const names = match[1]
|
|
73
|
+
.split(',')
|
|
74
|
+
.map((s) => s.trim())
|
|
75
|
+
.filter(Boolean);
|
|
76
|
+
players = names.map((name) => ({
|
|
77
|
+
uuid: '',
|
|
78
|
+
name,
|
|
79
|
+
created: '',
|
|
80
|
+
source: 'rcon',
|
|
81
|
+
expires: 'forever',
|
|
82
|
+
reason: '',
|
|
83
|
+
}));
|
|
84
|
+
source = 'rcon';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Fallback to JSON
|
|
89
|
+
if (players.length === 0 || source !== 'rcon') {
|
|
90
|
+
players = await readBannedPlayersJson(bannedPlayersPath);
|
|
91
|
+
source = 'json';
|
|
92
|
+
}
|
|
93
|
+
if (options.json) {
|
|
94
|
+
console.log(JSON.stringify({
|
|
95
|
+
server: options.serverName,
|
|
96
|
+
running: isRunning,
|
|
97
|
+
players: players.map((p) => ({
|
|
98
|
+
name: p.name,
|
|
99
|
+
reason: p.reason,
|
|
100
|
+
created: p.created,
|
|
101
|
+
})),
|
|
102
|
+
source,
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
console.log(colors.bold(`\nBanned Players for ${options.serverName}:\n`));
|
|
107
|
+
if (players.length === 0) {
|
|
108
|
+
console.log(' (none)');
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
for (const player of players) {
|
|
112
|
+
const reason = player.reason ? ` - ${player.reason}` : '';
|
|
113
|
+
console.log(` ${colors.red(player.name)}${colors.dim(reason)}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
console.log('');
|
|
117
|
+
if (!isRunning) {
|
|
118
|
+
log.warn('Server is not running. Showing bans from JSON file');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Ban a player
|
|
125
|
+
*/
|
|
126
|
+
async function banPlayer(options, bannedPlayersPath, containerName, isRunning) {
|
|
127
|
+
const playerName = options.target;
|
|
128
|
+
const reason = options.reason || 'Banned by operator';
|
|
129
|
+
// Check if already banned
|
|
130
|
+
const bannedPlayers = await readBannedPlayersJson(bannedPlayersPath);
|
|
131
|
+
if (findBannedPlayerByName(bannedPlayers, playerName)) {
|
|
132
|
+
if (options.json) {
|
|
133
|
+
console.log(JSON.stringify({
|
|
134
|
+
success: false,
|
|
135
|
+
error: 'already_banned',
|
|
136
|
+
player: playerName,
|
|
137
|
+
server: options.serverName,
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
log.warn(`${playerName} is already banned`);
|
|
142
|
+
}
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
let rconSuccess = false;
|
|
146
|
+
let rconMessage = '';
|
|
147
|
+
if (isRunning) {
|
|
148
|
+
const rconCmd = reason ? ['ban', playerName, reason] : ['ban', playerName];
|
|
149
|
+
const result = await execRconWithOutput(containerName, rconCmd);
|
|
150
|
+
rconSuccess = result.code === 0;
|
|
151
|
+
rconMessage = result.output.trim();
|
|
152
|
+
}
|
|
153
|
+
// Update JSON file for non-running server
|
|
154
|
+
if (!isRunning) {
|
|
155
|
+
const newEntry = {
|
|
156
|
+
uuid: '', // Will be populated by server on start
|
|
157
|
+
name: playerName,
|
|
158
|
+
created: createTimestamp(),
|
|
159
|
+
source: 'mcctl',
|
|
160
|
+
expires: 'forever',
|
|
161
|
+
reason,
|
|
162
|
+
};
|
|
163
|
+
bannedPlayers.push(newEntry);
|
|
164
|
+
await writeBannedPlayersJson(bannedPlayersPath, bannedPlayers);
|
|
165
|
+
}
|
|
166
|
+
if (options.json) {
|
|
167
|
+
console.log(JSON.stringify({
|
|
168
|
+
success: true,
|
|
169
|
+
player: playerName,
|
|
170
|
+
reason,
|
|
171
|
+
server: options.serverName,
|
|
172
|
+
rcon: isRunning ? { success: rconSuccess, message: rconMessage } : null,
|
|
173
|
+
running: isRunning,
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
if (isRunning && rconSuccess) {
|
|
178
|
+
console.log(colors.green(`${playerName} has been banned (applied immediately)`));
|
|
179
|
+
}
|
|
180
|
+
else if (isRunning && !rconSuccess) {
|
|
181
|
+
console.log(colors.yellow(`RCON: ${rconMessage}`));
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
console.log(colors.yellow(`Server is not running.`));
|
|
185
|
+
console.log(colors.green(`Ban saved. Will apply on next start.`));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Unban a player
|
|
192
|
+
*/
|
|
193
|
+
async function unbanPlayer(options, bannedPlayersPath, containerName, isRunning) {
|
|
194
|
+
const playerName = options.target;
|
|
195
|
+
let rconSuccess = false;
|
|
196
|
+
let rconMessage = '';
|
|
197
|
+
if (isRunning) {
|
|
198
|
+
const result = await execRconWithOutput(containerName, ['pardon', playerName]);
|
|
199
|
+
rconSuccess = result.code === 0;
|
|
200
|
+
rconMessage = result.output.trim();
|
|
201
|
+
}
|
|
202
|
+
// Update JSON file
|
|
203
|
+
const bannedPlayers = await readBannedPlayersJson(bannedPlayersPath);
|
|
204
|
+
const filteredPlayers = bannedPlayers.filter((p) => p.name.toLowerCase() !== playerName.toLowerCase());
|
|
205
|
+
if (filteredPlayers.length !== bannedPlayers.length) {
|
|
206
|
+
await writeBannedPlayersJson(bannedPlayersPath, filteredPlayers);
|
|
207
|
+
}
|
|
208
|
+
if (options.json) {
|
|
209
|
+
console.log(JSON.stringify({
|
|
210
|
+
success: true,
|
|
211
|
+
player: playerName,
|
|
212
|
+
server: options.serverName,
|
|
213
|
+
rcon: isRunning ? { success: rconSuccess, message: rconMessage } : null,
|
|
214
|
+
running: isRunning,
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
if (isRunning && rconSuccess) {
|
|
219
|
+
console.log(colors.green(`${playerName} has been unbanned (applied immediately)`));
|
|
220
|
+
}
|
|
221
|
+
else if (isRunning && !rconSuccess) {
|
|
222
|
+
console.log(colors.yellow(`RCON: ${rconMessage}`));
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
console.log(colors.yellow(`Server is not running.`));
|
|
226
|
+
console.log(colors.green(`Unban saved. Will apply on next start.`));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return 0;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Handle IP ban commands
|
|
233
|
+
*/
|
|
234
|
+
async function handleIpBan(options, bannedIpsPath, containerName, isRunning) {
|
|
235
|
+
const ipAction = options.ipAction;
|
|
236
|
+
if (!ipAction) {
|
|
237
|
+
log.error('IP action is required: list, add, remove');
|
|
238
|
+
log.info('Usage: mcctl ban <server> ip <list|add|remove> [ip] [reason]');
|
|
239
|
+
return 1;
|
|
240
|
+
}
|
|
241
|
+
switch (ipAction) {
|
|
242
|
+
case 'list':
|
|
243
|
+
return await listBannedIps(options, bannedIpsPath, containerName, isRunning);
|
|
244
|
+
case 'add':
|
|
245
|
+
if (!options.target) {
|
|
246
|
+
log.error('IP address is required');
|
|
247
|
+
log.info('Usage: mcctl ban <server> ip add <ip> [reason]');
|
|
248
|
+
return 1;
|
|
249
|
+
}
|
|
250
|
+
return await banIp(options, bannedIpsPath, containerName, isRunning);
|
|
251
|
+
case 'remove':
|
|
252
|
+
if (!options.target) {
|
|
253
|
+
log.error('IP address is required');
|
|
254
|
+
log.info('Usage: mcctl ban <server> ip remove <ip>');
|
|
255
|
+
return 1;
|
|
256
|
+
}
|
|
257
|
+
return await unbanIp(options, bannedIpsPath, containerName, isRunning);
|
|
258
|
+
default:
|
|
259
|
+
log.error(`Unknown IP action: ${ipAction}`);
|
|
260
|
+
return 1;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* List banned IPs
|
|
265
|
+
*/
|
|
266
|
+
async function listBannedIps(options, bannedIpsPath, containerName, isRunning) {
|
|
267
|
+
let ips = [];
|
|
268
|
+
let source = 'json';
|
|
269
|
+
if (isRunning) {
|
|
270
|
+
const result = await execRconWithOutput(containerName, ['banlist', 'ips']);
|
|
271
|
+
if (result.code === 0) {
|
|
272
|
+
const match = result.output.match(/:\s*(.+)$/);
|
|
273
|
+
if (match && match[1] && !match[1].includes('There are no')) {
|
|
274
|
+
const ipList = match[1]
|
|
275
|
+
.split(',')
|
|
276
|
+
.map((s) => s.trim())
|
|
277
|
+
.filter(Boolean);
|
|
278
|
+
ips = ipList.map((ip) => ({
|
|
279
|
+
ip,
|
|
280
|
+
created: '',
|
|
281
|
+
source: 'rcon',
|
|
282
|
+
expires: 'forever',
|
|
283
|
+
reason: '',
|
|
284
|
+
}));
|
|
285
|
+
source = 'rcon';
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (ips.length === 0 || source !== 'rcon') {
|
|
290
|
+
ips = await readBannedIpsJson(bannedIpsPath);
|
|
291
|
+
source = 'json';
|
|
292
|
+
}
|
|
293
|
+
if (options.json) {
|
|
294
|
+
console.log(JSON.stringify({
|
|
295
|
+
server: options.serverName,
|
|
296
|
+
running: isRunning,
|
|
297
|
+
ips: ips.map((entry) => ({
|
|
298
|
+
ip: entry.ip,
|
|
299
|
+
reason: entry.reason,
|
|
300
|
+
created: entry.created,
|
|
301
|
+
})),
|
|
302
|
+
source,
|
|
303
|
+
}));
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
console.log(colors.bold(`\nBanned IPs for ${options.serverName}:\n`));
|
|
307
|
+
if (ips.length === 0) {
|
|
308
|
+
console.log(' (none)');
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
for (const entry of ips) {
|
|
312
|
+
const reason = entry.reason ? ` - ${entry.reason}` : '';
|
|
313
|
+
console.log(` ${colors.red(entry.ip)}${colors.dim(reason)}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
console.log('');
|
|
317
|
+
}
|
|
318
|
+
return 0;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Ban an IP
|
|
322
|
+
*/
|
|
323
|
+
async function banIp(options, bannedIpsPath, containerName, isRunning) {
|
|
324
|
+
const ip = options.target;
|
|
325
|
+
const reason = options.reason || 'Banned by operator';
|
|
326
|
+
const bannedIps = await readBannedIpsJson(bannedIpsPath);
|
|
327
|
+
if (findBannedIp(bannedIps, ip)) {
|
|
328
|
+
if (options.json) {
|
|
329
|
+
console.log(JSON.stringify({
|
|
330
|
+
success: false,
|
|
331
|
+
error: 'already_banned',
|
|
332
|
+
ip,
|
|
333
|
+
server: options.serverName,
|
|
334
|
+
}));
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
log.warn(`${ip} is already banned`);
|
|
338
|
+
}
|
|
339
|
+
return 0;
|
|
340
|
+
}
|
|
341
|
+
let rconSuccess = false;
|
|
342
|
+
let rconMessage = '';
|
|
343
|
+
if (isRunning) {
|
|
344
|
+
const rconCmd = reason ? ['ban-ip', ip, reason] : ['ban-ip', ip];
|
|
345
|
+
const result = await execRconWithOutput(containerName, rconCmd);
|
|
346
|
+
rconSuccess = result.code === 0;
|
|
347
|
+
rconMessage = result.output.trim();
|
|
348
|
+
}
|
|
349
|
+
if (!isRunning) {
|
|
350
|
+
const newEntry = {
|
|
351
|
+
ip,
|
|
352
|
+
created: createTimestamp(),
|
|
353
|
+
source: 'mcctl',
|
|
354
|
+
expires: 'forever',
|
|
355
|
+
reason,
|
|
356
|
+
};
|
|
357
|
+
bannedIps.push(newEntry);
|
|
358
|
+
await writeBannedIpsJson(bannedIpsPath, bannedIps);
|
|
359
|
+
}
|
|
360
|
+
if (options.json) {
|
|
361
|
+
console.log(JSON.stringify({
|
|
362
|
+
success: true,
|
|
363
|
+
ip,
|
|
364
|
+
reason,
|
|
365
|
+
server: options.serverName,
|
|
366
|
+
rcon: isRunning ? { success: rconSuccess, message: rconMessage } : null,
|
|
367
|
+
running: isRunning,
|
|
368
|
+
}));
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
if (isRunning && rconSuccess) {
|
|
372
|
+
console.log(colors.green(`${ip} has been banned (applied immediately)`));
|
|
373
|
+
}
|
|
374
|
+
else if (!isRunning) {
|
|
375
|
+
console.log(colors.yellow(`Server is not running.`));
|
|
376
|
+
console.log(colors.green(`IP ban saved. Will apply on next start.`));
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return 0;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Unban an IP
|
|
383
|
+
*/
|
|
384
|
+
async function unbanIp(options, bannedIpsPath, containerName, isRunning) {
|
|
385
|
+
const ip = options.target;
|
|
386
|
+
let rconSuccess = false;
|
|
387
|
+
let rconMessage = '';
|
|
388
|
+
if (isRunning) {
|
|
389
|
+
const result = await execRconWithOutput(containerName, ['pardon-ip', ip]);
|
|
390
|
+
rconSuccess = result.code === 0;
|
|
391
|
+
rconMessage = result.output.trim();
|
|
392
|
+
}
|
|
393
|
+
const bannedIps = await readBannedIpsJson(bannedIpsPath);
|
|
394
|
+
const filteredIps = bannedIps.filter((entry) => entry.ip !== ip);
|
|
395
|
+
if (filteredIps.length !== bannedIps.length) {
|
|
396
|
+
await writeBannedIpsJson(bannedIpsPath, filteredIps);
|
|
397
|
+
}
|
|
398
|
+
if (options.json) {
|
|
399
|
+
console.log(JSON.stringify({
|
|
400
|
+
success: true,
|
|
401
|
+
ip,
|
|
402
|
+
server: options.serverName,
|
|
403
|
+
rcon: isRunning ? { success: rconSuccess, message: rconMessage } : null,
|
|
404
|
+
running: isRunning,
|
|
405
|
+
}));
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
if (isRunning && rconSuccess) {
|
|
409
|
+
console.log(colors.green(`${ip} has been unbanned (applied immediately)`));
|
|
410
|
+
}
|
|
411
|
+
else if (!isRunning) {
|
|
412
|
+
console.log(colors.yellow(`Server is not running.`));
|
|
413
|
+
console.log(colors.green(`IP unban saved. Will apply on next start.`));
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return 0;
|
|
417
|
+
}
|
|
418
|
+
//# sourceMappingURL=ban.js.map
|