@ -0,0 +1,32 @@ |
|||||||
|
FROM ubuntu:latest |
||||||
|
|
||||||
|
WORKDIR /app |
||||||
|
|
||||||
|
COPY ./run.sh ./run.sh |
||||||
|
|
||||||
|
RUN apt -y update |
||||||
|
RUN apt -y upgrade |
||||||
|
|
||||||
|
RUN apt -y install sudo software-properties-common |
||||||
|
RUN dpkg --add-architecture i386 |
||||||
|
RUN apt-add-repository multiverse |
||||||
|
RUN apt -y update |
||||||
|
RUN echo steam steam/question select "I AGREE" | sudo debconf-set-selections |
||||||
|
RUN echo steam steam/license note '' | sudo debconf-set-selections |
||||||
|
RUN apt install -y steamcmd lib32gcc-s1 |
||||||
|
RUN echo "fs.file-max=100000" > /etc/sysctl.conf |
||||||
|
RUN echo "* soft nofile 100000" > /etc/security/limits.conf |
||||||
|
RUN echo "* hard nofile 100000" > /etc/security/limits.conf |
||||||
|
RUN echo "session required pam_limits.so" > /etc/pam.d/common-session |
||||||
|
|
||||||
|
RUN useradd -m ark |
||||||
|
RUN chown -R ark:ark . |
||||||
|
RUN sudo -u ark -s |
||||||
|
|
||||||
|
RUN chmod +x ./run.sh |
||||||
|
RUN ulimit -n 100000 |
||||||
|
RUN /usr/games/steamcmd +force_install_dir /app +login anonymous +app_update 376030 validate +exit |
||||||
|
#RUN chmod +x ./ShooterGame/Binaries/Linux/ShooterGameServer |
||||||
|
|
||||||
|
|
||||||
|
ENTRYPOINT [ "./run.sh" ] |
@ -0,0 +1,4 @@ |
|||||||
|
#!/bin/bash |
||||||
|
|
||||||
|
#./ShooterGame/Binaries/Linux/ShooterGameServer TheIsland?listen?SessionName=ArkServer?ServerPassword=123456?ServerAdminPassword=123456 -server -log -crossplay |
||||||
|
./ShooterGame/Binaries/Linux/ShooterGameServer TheIsland?listen?SessionName=ArkServer -server -log -crossplay |
@ -0,0 +1,18 @@ |
|||||||
|
FROM ubuntu:latest |
||||||
|
|
||||||
|
WORKDIR /app |
||||||
|
|
||||||
|
ENV LD_LIBRARY_PATH=. |
||||||
|
|
||||||
|
COPY ./bedrock-server-1.21.60.10.zip ./bedrock-server.zip |
||||||
|
|
||||||
|
RUN apt -y update |
||||||
|
RUN apt -y upgrade |
||||||
|
RUN apt install -y 7zip curl |
||||||
|
RUN 7z x ./bedrock-server.zip |
||||||
|
RUN chmod +x ./bedrock_server |
||||||
|
RUN rm -rf ./bedrock-server.zip |
||||||
|
VOLUME [ "/app" ] |
||||||
|
|
||||||
|
ENTRYPOINT [ "./bedrock_server" ] |
||||||
|
|
@ -0,0 +1,34 @@ |
|||||||
|
<?php |
||||||
|
namespace App\Console; |
||||||
|
use Illuminate\Console\Scheduling\Schedule; |
||||||
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel; |
||||||
|
use Illuminate\Support\Facades\File; |
||||||
|
use Illuminate\Support\Facades\Log; |
||||||
|
use Illuminate\Support\Facades\Storage; |
||||||
|
|
||||||
|
class Kernel extends ConsoleKernel |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Define the application's command schedule. |
||||||
|
* |
||||||
|
* @param \Illuminate\Console\Scheduling\Schedule $schedule |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
protected function schedule(Schedule $schedule) |
||||||
|
{ |
||||||
|
|
||||||
|
$schedule->call(function() { |
||||||
|
Log::info("Hello"); |
||||||
|
})->everyThreeMinutes(); |
||||||
|
} |
||||||
|
/** |
||||||
|
* Register the commands for the application. |
||||||
|
* |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
protected function commands() |
||||||
|
{ |
||||||
|
$this->load(__DIR__.'/Commands'); |
||||||
|
require base_path('routes/console.php'); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Docker; |
||||||
|
|
||||||
|
use Exception; |
||||||
|
use Illuminate\Support\Facades\Log; |
||||||
|
use Psr\Http\Message\ResponseInterface; |
||||||
|
use React\Http\Browser; |
||||||
|
use React\Socket\FixedUriConnector; |
||||||
|
use React\Socket\UnixConnector; |
||||||
|
use function React\Async\await; |
||||||
|
|
||||||
|
|
||||||
|
class Exec |
||||||
|
{ |
||||||
|
protected $id; |
||||||
|
protected $connector; |
||||||
|
protected $browser; |
||||||
|
|
||||||
|
public function __construct($id) |
||||||
|
{ |
||||||
|
$this->id = $id; |
||||||
|
$connection = Docker::connect(); |
||||||
|
$this->connector = $connection->connector; |
||||||
|
$this->browser = $connection->browser; |
||||||
|
} |
||||||
|
|
||||||
|
public function getId() { return $this->id; } |
||||||
|
|
||||||
|
public function start($args = []) |
||||||
|
{ |
||||||
|
try { |
||||||
|
Log::info("Starting exec :" . Docker::endpoint("/exec/" . $this->id . "/start")); |
||||||
|
$response = await($this->browser->post( |
||||||
|
Docker::endpoint("/exec/" . $this->id . "/start"), |
||||||
|
[ "Content-Type" => "application/json"], |
||||||
|
)); |
||||||
|
return json_decode($response->getBody()); |
||||||
|
} catch (Exception $e) { |
||||||
|
throw $e; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function inspect() |
||||||
|
{ |
||||||
|
try { |
||||||
|
$response = await($this->browser->get( |
||||||
|
Docker::endpoint("/exec/" . $this->id . "/json"), |
||||||
|
[ "Content-Type" => "text/plain" ] |
||||||
|
)); |
||||||
|
return json_decode($response->getBody()); |
||||||
|
} catch (Exception $e) { |
||||||
|
throw $e; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Http\Controllers; |
||||||
|
|
||||||
|
use Illuminate\Http\Request; |
||||||
|
use Illuminate\Support\Str; |
||||||
|
|
||||||
|
use App\Docker\Container; |
||||||
|
use App\Jobs\InitServer; |
||||||
|
use App\Models\ExposedPort; |
||||||
|
use App\Models\Server; |
||||||
|
use App\Models\Service; |
||||||
|
use Exception; |
||||||
|
use Illuminate\Support\Facades\Auth; |
||||||
|
use Inertia\Inertia; |
||||||
|
|
||||||
|
|
||||||
|
class ServerController extends Controller |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Display a listing of the resource. |
||||||
|
*/ |
||||||
|
public function index() |
||||||
|
{ |
||||||
|
return Inertia::render("Server/Index", [ |
||||||
|
"servers" => Server::all()->jsonSerialize(), |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Show the form for creating a new resource. |
||||||
|
*/ |
||||||
|
public function create() |
||||||
|
{ |
||||||
|
return Inertia::render('Server/Create', [ |
||||||
|
"services" => Service::all() |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Store a newly created resource in storage. |
||||||
|
*/ |
||||||
|
public function store(Request $request) |
||||||
|
{ |
||||||
|
$request->validate([ |
||||||
|
"name" => "required|string", |
||||||
|
"service" => "required|string", |
||||||
|
]); |
||||||
|
|
||||||
|
$service = Service::where("uuid", $request->service)->firstOrFail(); |
||||||
|
$server = Server::create([ |
||||||
|
"uuid" => Str::uuid(), |
||||||
|
"name" => preg_replace('/[^a-zA-Z0-9_.-]/', '', $request->name), |
||||||
|
"service_id" => $service->id, |
||||||
|
"status_id" => 3, |
||||||
|
"user_id" => Auth::user()->id |
||||||
|
]); |
||||||
|
dispatch(new InitServer($server)); |
||||||
|
return redirect(route("servers.index")); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Display the specified resource. |
||||||
|
*/ |
||||||
|
public function show(Request $request) |
||||||
|
{ |
||||||
|
$server = Server::where("uuid", $request->id)->firstOrFail(); |
||||||
|
return Inertia::render("Server/Show", [ |
||||||
|
"server" => $server |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Show the form for editing the specified resource. |
||||||
|
*/ |
||||||
|
public function edit(string $id) |
||||||
|
{ |
||||||
|
// |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Update the specified resource in storage. |
||||||
|
*/ |
||||||
|
public function update(Request $request, string $id) |
||||||
|
{ |
||||||
|
// |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Remove the specified resource from storage. |
||||||
|
*/ |
||||||
|
public function destroy(string $id) |
||||||
|
{ |
||||||
|
// |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Jobs; |
||||||
|
|
||||||
|
use App\Docker\Container; |
||||||
|
use App\Models\ExposedPort; |
||||||
|
use Exception; |
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue; |
||||||
|
use Illuminate\Foundation\Queue\Queueable; |
||||||
|
use Illuminate\Support\Facades\Log; |
||||||
|
use Illuminate\Support\Str; |
||||||
|
|
||||||
|
class InitServer implements ShouldQueue |
||||||
|
{ |
||||||
|
use Queueable; |
||||||
|
|
||||||
|
protected $server; |
||||||
|
/** |
||||||
|
* Create a new job instance. |
||||||
|
*/ |
||||||
|
public function __construct($server) |
||||||
|
{ |
||||||
|
$this->server = $server; |
||||||
|
$server->update(["status_id" => 2]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Execute the job. |
||||||
|
*/ |
||||||
|
public function handle(): void |
||||||
|
{ |
||||||
|
try { |
||||||
|
$config = config($this->server->service->config); |
||||||
|
$ports = explode("|", $this->server->service->ports); |
||||||
|
for ($i = 0; $i < count($ports); $i++) { |
||||||
|
$port = ExposedPort::where("usable", true)->first(); |
||||||
|
if(!$port) throw new Exception("All ports are used, please try later."); |
||||||
|
$this->server->exposedPorts()->attach($port->id); |
||||||
|
$port->update(["usable" => false]); |
||||||
|
$config["ExposedPorts"][$ports[$i] . "/" . $this->server->service->protocol] = (object)[]; |
||||||
|
$config["HostConfig"]["PortBindings"][$ports[$i] . "/" . $this->server->service->protocol] = [[ "HostPort" => "" . $port->number ]]; |
||||||
|
} |
||||||
|
Log::info(json_encode($config, JSON_UNESCAPED_SLASHES)); |
||||||
|
$container = Container::create($this->server->name . Str::random(5), $config); |
||||||
|
$container->start(); |
||||||
|
$this->server->update([ |
||||||
|
"container" => $container->getId(), |
||||||
|
"status_id" => 1, |
||||||
|
"start" => now() |
||||||
|
]); |
||||||
|
}catch(Exception $e) { |
||||||
|
$this->fail($e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function fail($e): void |
||||||
|
{ |
||||||
|
$this->server->update([ |
||||||
|
"status_id" => 4, |
||||||
|
"end" => now() |
||||||
|
]); |
||||||
|
$this->server->exposedPorts()->detach(); |
||||||
|
Log::info($e->getResponse()->getBody()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Models; |
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model; |
||||||
|
|
||||||
|
class ExposedPort extends Model |
||||||
|
{ |
||||||
|
protected $fillable = [ |
||||||
|
'number', |
||||||
|
'usable' |
||||||
|
]; |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Models; |
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model; |
||||||
|
|
||||||
|
class Server extends Model |
||||||
|
{ |
||||||
|
protected $fillable = [ |
||||||
|
"uuid", |
||||||
|
'name', |
||||||
|
"container", |
||||||
|
"start", |
||||||
|
"end", |
||||||
|
"service_id", |
||||||
|
"user_id", |
||||||
|
"status_id", |
||||||
|
]; |
||||||
|
|
||||||
|
public function jsonSerialize(): mixed |
||||||
|
{ |
||||||
|
return [ |
||||||
|
"uuid" => $this->uuid, |
||||||
|
'name' => $this->name, |
||||||
|
"start" => $this->start, |
||||||
|
"end" => $this->end, |
||||||
|
"service" => $this->service, |
||||||
|
"user" => $this->user, |
||||||
|
"status" => $this->status, |
||||||
|
"ports" => $this->exposedPorts()->pluck("number"), |
||||||
|
]; |
||||||
|
} |
||||||
|
|
||||||
|
public function exposedPorts() |
||||||
|
{ |
||||||
|
return $this->belongsToMany(ExposedPort::class); |
||||||
|
} |
||||||
|
|
||||||
|
public function service() |
||||||
|
{ |
||||||
|
return $this->belongsTo(Service::class); |
||||||
|
} |
||||||
|
|
||||||
|
public function status() |
||||||
|
{ |
||||||
|
return $this->belongsTo(Status::class); |
||||||
|
} |
||||||
|
|
||||||
|
public function user() |
||||||
|
{ |
||||||
|
return $this->belongsTo(User::class); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Models; |
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model; |
||||||
|
|
||||||
|
class Service extends Model |
||||||
|
{ |
||||||
|
protected $fillable = [ |
||||||
|
"uuid", |
||||||
|
'name', |
||||||
|
"image", |
||||||
|
"config", |
||||||
|
"ports", |
||||||
|
"protocol" |
||||||
|
]; |
||||||
|
|
||||||
|
public function servers() |
||||||
|
{ |
||||||
|
return $this->hasMany(Server::class); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Models; |
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model; |
||||||
|
|
||||||
|
class Spec extends Model |
||||||
|
{ |
||||||
|
protected $fillable = [ |
||||||
|
"uuid", |
||||||
|
"cpus", |
||||||
|
"memory", |
||||||
|
"storage" |
||||||
|
]; |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Models; |
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model; |
||||||
|
|
||||||
|
class Status extends Model |
||||||
|
{ |
||||||
|
protected $fillable = [ |
||||||
|
'title', |
||||||
|
'message' |
||||||
|
]; |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
|
||||||
|
class Netstat |
||||||
|
{ |
||||||
|
|
||||||
|
public static function parseArgs($ssOutput) |
||||||
|
{ |
||||||
|
$lines = explode("\n", $ssOutput); |
||||||
|
|
||||||
|
// Step 2: Skip the header line |
||||||
|
array_shift($lines); |
||||||
|
|
||||||
|
// Step 3: Parse each line and extract relevant details |
||||||
|
$connections = []; |
||||||
|
foreach ($lines as $line) { |
||||||
|
// Ignore empty lines |
||||||
|
if (empty(trim($line))) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// Use regex to match the structured output |
||||||
|
if (preg_match('/(\S+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+users:\(\("([^"]+)",pid=(\d+),fd=(\d+)\)\)/', $line, $matches)) { |
||||||
|
$connections[] = [ |
||||||
|
'State' => $matches[1], // e.g., LISTEN |
||||||
|
'Recv-Q' => $matches[2], // e.g., 0 |
||||||
|
'Send-Q' => $matches[3], // e.g., 128 |
||||||
|
'LocalAddress' => $matches[4], // e.g., 0.0.0.0:80 |
||||||
|
'ForeignAddress' => $matches[5], // e.g., 0.0.0.0:* |
||||||
|
'Program' => $matches[6], // e.g., nginx |
||||||
|
'PID' => $matches[7], // e.g., 1234 |
||||||
|
'FD' => $matches[8], // e.g., 6 |
||||||
|
]; |
||||||
|
} |
||||||
|
} |
||||||
|
return $connections; |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,45 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
return [ |
||||||
|
"endpoint" => "http://localhost", |
||||||
|
"minecraftJava" => [ |
||||||
|
"Hostname" => "mincraft-server", |
||||||
|
"User" => "root", |
||||||
|
"Image" => "minecraftjava:latest", |
||||||
|
"AttachStdout" => false, |
||||||
|
"AttachStderr" => false, |
||||||
|
"OpenStdin" => true, |
||||||
|
"Tty" => true, |
||||||
|
"ExposedPorts" => [], |
||||||
|
"HostConfig" => [ |
||||||
|
"PortBindings" => [], |
||||||
|
] |
||||||
|
], |
||||||
|
"minecraftBedrock" => [ |
||||||
|
"Hostname" => "mincraft-server", |
||||||
|
"User" => "root", |
||||||
|
"Image" => "minecraftbedrock:latest", |
||||||
|
"OpenStdin" => true, |
||||||
|
"AttachStdout" => false, |
||||||
|
"AttachStderr" => false, |
||||||
|
"Tty" => true, |
||||||
|
"ExposedPorts" => [], |
||||||
|
"HostConfig" => [ |
||||||
|
"PortBindings" => [], |
||||||
|
] |
||||||
|
], |
||||||
|
"arkserver" => [ |
||||||
|
"Hostname" => "ark-server", |
||||||
|
"User" => "root", |
||||||
|
"Image" => "arkserver:latest", |
||||||
|
"OpenStdin" => true, |
||||||
|
"AttachStdout" => false, |
||||||
|
"AttachStderr" => false, |
||||||
|
"Tty" => true, |
||||||
|
"ExposedPorts" => [], |
||||||
|
"HostConfig" => [ |
||||||
|
"PortBindings" => [], |
||||||
|
] |
||||||
|
] |
||||||
|
|
||||||
|
]; |
@ -0,0 +1,32 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
use App\Models\ExposedPort; |
||||||
|
use Illuminate\Database\Migrations\Migration; |
||||||
|
use Illuminate\Database\Schema\Blueprint; |
||||||
|
use Illuminate\Support\Facades\DB; |
||||||
|
use Illuminate\Support\Facades\Schema; |
||||||
|
|
||||||
|
return new class extends Migration |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Run the migrations. |
||||||
|
*/ |
||||||
|
public function up(): void |
||||||
|
{ |
||||||
|
Schema::create('exposed_ports', function (Blueprint $table) { |
||||||
|
$table->id(); |
||||||
|
$table->unsignedSmallInteger("number"); |
||||||
|
$table->boolean("usable"); |
||||||
|
$table->timestamps(); |
||||||
|
}); |
||||||
|
for($i = 25000; $i < 30000; $i++) ExposedPort::create([ "number" => $i, "usable" => true ]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reverse the migrations. |
||||||
|
*/ |
||||||
|
public function down(): void |
||||||
|
{ |
||||||
|
Schema::dropIfExists('exposed_ports'); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,62 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
use App\Models\Service; |
||||||
|
use Illuminate\Database\Migrations\Migration; |
||||||
|
use Illuminate\Database\Schema\Blueprint; |
||||||
|
use Illuminate\Support\Facades\Schema; |
||||||
|
use Illuminate\Support\Str; |
||||||
|
|
||||||
|
return new class extends Migration |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Run the migrations. |
||||||
|
*/ |
||||||
|
public function up(): void |
||||||
|
{ |
||||||
|
Schema::create('services', function (Blueprint $table) { |
||||||
|
$table->id(); |
||||||
|
$table->uuid(); |
||||||
|
$table->string("name"); |
||||||
|
$table->string("image"); |
||||||
|
$table->string("config"); |
||||||
|
$table->string("protocol"); |
||||||
|
$table->string("ports"); |
||||||
|
$table->timestamps(); |
||||||
|
}); |
||||||
|
|
||||||
|
Service::create([ |
||||||
|
"uuid" => Str::uuid(), |
||||||
|
"name" => "Minecraft Java edition", |
||||||
|
"image" => "/img/minecraft-java.webp", |
||||||
|
"config" => "docker.minecraftJava", |
||||||
|
"protocol" => "tcp", |
||||||
|
"ports" => "25565" |
||||||
|
]); |
||||||
|
|
||||||
|
Service::create([ |
||||||
|
"uuid" => Str::uuid(), |
||||||
|
"name" => "Minecraft Bedrock edition", |
||||||
|
"image" => "/img/minecraft-bedrock.webp", |
||||||
|
"config" => "docker.minecraftBedrock", |
||||||
|
"protocol" => "udp", |
||||||
|
"ports" => "19132|19133" |
||||||
|
]); |
||||||
|
|
||||||
|
Service::create([ |
||||||
|
"uuid" => Str::uuid(), |
||||||
|
"name" => "Ark Survial Evolved", |
||||||
|
"image" => "/img/ark.jpeg", |
||||||
|
"config" => "docker.arkserver", |
||||||
|
"protocol" => "udp", |
||||||
|
"ports" => "27015|7777" |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reverse the migrations. |
||||||
|
*/ |
||||||
|
public function down(): void |
||||||
|
{ |
||||||
|
Schema::dropIfExists('services'); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,50 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
use App\Models\Status; |
||||||
|
use Illuminate\Database\Migrations\Migration; |
||||||
|
use Illuminate\Database\Schema\Blueprint; |
||||||
|
use Illuminate\Support\Facades\Schema; |
||||||
|
|
||||||
|
return new class extends Migration |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Run the migrations. |
||||||
|
*/ |
||||||
|
public function up(): void |
||||||
|
{ |
||||||
|
Schema::create('statuses', function (Blueprint $table) { |
||||||
|
$table->id(); |
||||||
|
$table->string("title"); |
||||||
|
$table->string("message"); |
||||||
|
$table->timestamps(); |
||||||
|
}); |
||||||
|
|
||||||
|
Status::create([ |
||||||
|
"title" => "Running", |
||||||
|
"message" => "", |
||||||
|
]); |
||||||
|
|
||||||
|
Status::create([ |
||||||
|
"title" => "Pending", |
||||||
|
"message" => "", |
||||||
|
]); |
||||||
|
|
||||||
|
Status::create([ |
||||||
|
"title" => "Offline", |
||||||
|
"message" => "", |
||||||
|
]); |
||||||
|
|
||||||
|
Status::create([ |
||||||
|
"title" => "Failed", |
||||||
|
"message" => "", |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reverse the migrations. |
||||||
|
*/ |
||||||
|
public function down(): void |
||||||
|
{ |
||||||
|
Schema::dropIfExists('server_statuses'); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,54 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
use App\Models\Spec; |
||||||
|
use Illuminate\Database\Migrations\Migration; |
||||||
|
use Illuminate\Database\Schema\Blueprint; |
||||||
|
use Illuminate\Support\Facades\Schema; |
||||||
|
use Illuminate\Support\Str; |
||||||
|
|
||||||
|
return new class extends Migration |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Run the migrations. |
||||||
|
*/ |
||||||
|
public function up(): void |
||||||
|
{ |
||||||
|
Schema::create('specs', function (Blueprint $table) { |
||||||
|
$table->id(); |
||||||
|
$table->uuid("uuid"); |
||||||
|
$table->unsignedTinyInteger("cpus"); |
||||||
|
$table->unsignedBigInteger("memory"); |
||||||
|
$table->unsignedBigInteger("storage"); |
||||||
|
$table->timestamps(); |
||||||
|
}); |
||||||
|
|
||||||
|
Spec::create([ |
||||||
|
"uuid" => Str::uuid(), |
||||||
|
"cpus" => 1, |
||||||
|
"memory" => 2000000000, |
||||||
|
"storage" => 20000000000 |
||||||
|
]); |
||||||
|
|
||||||
|
Spec::create([ |
||||||
|
"uuid" => Str::uuid(), |
||||||
|
"cpus" => 1, |
||||||
|
"memory" => 3000000000, |
||||||
|
"storage" => 50000000000 |
||||||
|
]); |
||||||
|
|
||||||
|
Spec::create([ |
||||||
|
"uuid" => Str::uuid(), |
||||||
|
"cpus" => 2, |
||||||
|
"memory" => 5000000000, |
||||||
|
"storage" => 40000000000 |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reverse the migrations. |
||||||
|
*/ |
||||||
|
public function down(): void |
||||||
|
{ |
||||||
|
Schema::dropIfExists('specs'); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,39 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration; |
||||||
|
use Illuminate\Database\Schema\Blueprint; |
||||||
|
use Illuminate\Support\Facades\Schema; |
||||||
|
|
||||||
|
return new class extends Migration |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Run the migrations. |
||||||
|
*/ |
||||||
|
public function up(): void |
||||||
|
{ |
||||||
|
Schema::create('servers', function (Blueprint $table) { |
||||||
|
$table->id(); |
||||||
|
$table->uuid("uuid"); |
||||||
|
$table->string("container", 511)->nullable(); |
||||||
|
$table->string("name"); |
||||||
|
$table->dateTime("start")->nullable(); |
||||||
|
$table->dateTime("end")->nullable(); |
||||||
|
$table->unsignedBigInteger("service_id"); |
||||||
|
$table->unsignedBigInteger("user_id"); |
||||||
|
$table->unsignedBigInteger("status_id"); |
||||||
|
$table->timestamps(); |
||||||
|
|
||||||
|
$table->foreign("service_id")->references("id")->on("services"); |
||||||
|
$table->foreign("user_id")->references("id")->on("users"); |
||||||
|
$table->foreign("status_id")->references("id")->on("statuses"); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reverse the migrations. |
||||||
|
*/ |
||||||
|
public function down(): void |
||||||
|
{ |
||||||
|
Schema::dropIfExists('servers'); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,32 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration; |
||||||
|
use Illuminate\Database\Schema\Blueprint; |
||||||
|
use Illuminate\Support\Facades\Schema; |
||||||
|
|
||||||
|
return new class extends Migration |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Run the migrations. |
||||||
|
*/ |
||||||
|
public function up(): void |
||||||
|
{ |
||||||
|
Schema::create('exposed_port_server', function (Blueprint $table) { |
||||||
|
$table->id(); |
||||||
|
$table->unsignedBigInteger("server_id"); |
||||||
|
$table->unsignedBigInteger("exposed_port_id")->unique(); |
||||||
|
$table->timestamps(); |
||||||
|
|
||||||
|
$table->foreign("server_id")->references("id")->on("servers"); |
||||||
|
$table->foreign("exposed_port_id")->references("id")->on("exposed_ports"); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reverse the migrations. |
||||||
|
*/ |
||||||
|
public function down(): void |
||||||
|
{ |
||||||
|
Schema::dropIfExists('exposed_port_server'); |
||||||
|
} |
||||||
|
}; |
After Width: | Height: | Size: 463 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 713 B |
After Width: | Height: | Size: 859 B |
After Width: | Height: | Size: 924 B |
After Width: | Height: | Size: 995 B |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 846 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 376 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 399 B |
After Width: | Height: | Size: 456 B |
After Width: | Height: | Size: 137 KiB |
After Width: | Height: | Size: 714 KiB |
After Width: | Height: | Size: 151 KiB |
After Width: | Height: | Size: 148 KiB |
After Width: | Height: | Size: 67 KiB |
@ -1,30 +0,0 @@ |
|||||||
<script setup> |
|
||||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue'; |
|
||||||
import { Head } from '@inertiajs/vue3'; |
|
||||||
</script> |
|
||||||
|
|
||||||
<template> |
|
||||||
<Head title="Dashboard" /> |
|
||||||
|
|
||||||
<AuthenticatedLayout> |
|
||||||
<template #header> |
|
||||||
<h2 |
|
||||||
class="text-xl font-semibold leading-tight text-textColor-800" |
|
||||||
> |
|
||||||
Dashboard |
|
||||||
</h2> |
|
||||||
</template> |
|
||||||
|
|
||||||
<div class="py-12"> |
|
||||||
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8"> |
|
||||||
<div |
|
||||||
class="overflow-hidden bg-white shadow-sm sm:rounded-lg" |
|
||||||
> |
|
||||||
<div class="p-6 text-textColor-900"> |
|
||||||
You're logged in! |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</AuthenticatedLayout> |
|
||||||
</template> |
|
@ -0,0 +1,29 @@ |
|||||||
|
<script setup> |
||||||
|
import Layout from '@/Layouts/Layout.vue'; |
||||||
|
import { Head } from '@inertiajs/vue3'; |
||||||
|
import ServerElement from './Paritals/ServerElement.vue'; |
||||||
|
|
||||||
|
const props = defineProps({ |
||||||
|
servers: { |
||||||
|
type: Array, |
||||||
|
default: [] |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<Head title="Spawn" /> |
||||||
|
<Layout> |
||||||
|
<!-- En-tête --> |
||||||
|
<header class="max-w-6xl mx-auto my-10 text-center"> |
||||||
|
<h1 class="text-3xl font-bold animate-fade-in">Liste des Serveurs</h1> |
||||||
|
<p class="text-gray-400 mt-2">Gérez vos serveurs Minecraft, Ark, Rust et plus encore.</p> |
||||||
|
</header> |
||||||
|
|
||||||
|
<!-- Liste des serveurs --> |
||||||
|
<section class="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 my-10"> |
||||||
|
<ServerElement v-for="server in props.servers" :server="server"/> |
||||||
|
</section> |
||||||
|
</Layout> |
||||||
|
</template> |
@ -0,0 +1,49 @@ |
|||||||
|
<script setup> |
||||||
|
import { Link } from '@inertiajs/vue3'; |
||||||
|
|
||||||
|
|
||||||
|
const props = defineProps({ |
||||||
|
server: { |
||||||
|
type: Object, |
||||||
|
required: true |
||||||
|
} |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<!-- Carte Serveur --> |
||||||
|
<div class="bg-gray-800 rounded-lg shadow-lg p-6 slide-up relative"> |
||||||
|
<div class="flex justify-between items-center"> |
||||||
|
<h2 class="text-xl font-semibold flex items-center gap-2"> |
||||||
|
<img src="/icons/server-icon.svg" alt="Serveur" class="w-6 h-6 rotate-on-hover invert"> |
||||||
|
Serveur {{ props.server.name }} |
||||||
|
</h2> |
||||||
|
<span v-if="props.server.status.id < 3" class="px-3 py-1 text-sm font-bold rounded-lg bg-green-500 text-white">{{ props.server.status.title }}</span> |
||||||
|
<span v-else class="px-3 py-1 text-sm font-bold rounded-lg bg-red-500 text-white">{{ props.server.status.title }}</span> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="mt-4 space-y-2 text-sm"> |
||||||
|
<p><span class="font-medium">Type </span> {{ props.server.service.name }}</p> |
||||||
|
<p><span class="font-medium">Dernier démarrage </span> {{ props.server.start ? props.server.start : "aucun" }}</p> |
||||||
|
<p class="flex gap-2"> |
||||||
|
<span class="font-medium">Lien :</span> |
||||||
|
<p class="text-green-400 hover:underline"> |
||||||
|
{{ props.server.ports.length ? "hosting.anulax.ch:" + props.server.ports[0] : "Indisponible" }} |
||||||
|
</p> |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- Actions --> |
||||||
|
<div class="mt-4 flex gap-2 w-full justify-end"> |
||||||
|
<a href="" class="bg-green-500 hover:bg-green-600 text-white font-bold p-2 rounded-lg shadow-lg flex items-center gap-2"> |
||||||
|
<img src="/icons/start-icon.svg" alt="Start" class="w-5 h-5 invert"> |
||||||
|
</a> |
||||||
|
<a href="#" class="bg-red-500 hover:bg-red-600 text-white font-bold p-2 rounded-lg shadow-lg flex items-center gap-2"> |
||||||
|
<img src="/icons/stop-icon.svg" alt="Stop" class="w-5 h-5 invert"> |
||||||
|
</a> |
||||||
|
<Link :href="route('servers.show', props.server.uuid)" class="bg-blue-500 hover:bg-blue-600 text-white font-bold p-2 rounded-lg shadow-lg flex items-center gap-2"> |
||||||
|
<img src="/icons/details-icon.svg" alt="Détails" class="w-5 h-5 invert"> |
||||||
|
</Link> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
@ -0,0 +1,132 @@ |
|||||||
|
<script setup> |
||||||
|
import Layout from '@/Layouts/Layout.vue'; |
||||||
|
import { Head } from '@inertiajs/vue3'; |
||||||
|
|
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<Head title="Spawn" /> |
||||||
|
<Layout> |
||||||
|
<!-- En-tête --> |
||||||
|
<header class="my-10 text-center"> |
||||||
|
<h1 class="text-4xl font-bold flex items-center justify-center gap-2 fade-in"> |
||||||
|
Dashboard - Détails du Serveur |
||||||
|
</h1> |
||||||
|
<p class="text-gray-300 mt-4 fade-in"> |
||||||
|
Gérez et visualisez les informations clés de votre serveur en un coup d'œil. |
||||||
|
</p> |
||||||
|
</header> |
||||||
|
|
||||||
|
<!-- Cartes d'information --> |
||||||
|
<main class="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-6 mb-10 slide-up"> |
||||||
|
<!-- Carte : Informations Générales --> |
||||||
|
<div class="bg-gray-800 rounded-lg shadow-lg p-6"> |
||||||
|
<h2 class="text-2xl font-semibold border-b border-gray-700 pb-2 mb-4 flex items-center gap-2"> |
||||||
|
<img src="/icons/server-icon.svg" alt="Général" class="w-6 h-6 rotate-on-hover glow-on-hover invert"> |
||||||
|
Informations Générales |
||||||
|
</h2> |
||||||
|
<div class="space-y-3 text-sm"> |
||||||
|
<div class="flex justify-between"> |
||||||
|
<span class="font-medium">Nom</span> |
||||||
|
<span class="text-green-400 font-bold">Mon Serveur Minecraft</span> |
||||||
|
</div> |
||||||
|
<div class="flex justify-between"> |
||||||
|
<span class="font-medium">Type de jeu</span> |
||||||
|
<span>Minecraft</span> |
||||||
|
</div> |
||||||
|
<div class="flex flex-col gap-2"> |
||||||
|
<div class="flex justify-between items-center mb-1"> |
||||||
|
<div class="flex items-center gap-2"> |
||||||
|
<span class="font-medium">Lien d'accès</span> |
||||||
|
</div> |
||||||
|
<div class="flex items-center gap-2"> |
||||||
|
<a href="http://hosting.anulax.ch:25005" class="text-green-400 hover:underline"> |
||||||
|
hosting.anulax.ch:25005 |
||||||
|
</a> |
||||||
|
<button class="bg-blue-500 hover:bg-blue-600 text-white font-bold p-1 rounded-lg shadow-lg glow-on-hover flex items-center"> |
||||||
|
<img src="/icons/copy-icon.svg" alt="Copier" class="w-5 h-5 invert"> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- Carte : Activité --> |
||||||
|
<div class="bg-gray-800 rounded-lg shadow-lg p-6"> |
||||||
|
<h2 class="text-2xl font-semibold border-b border-gray-700 pb-2 mb-4 flex items-center gap-2"> |
||||||
|
<img src="/icons/stats-icon.svg" alt="Activité" class="w-6 h-6 rotate-on-hover glow-on-hover invert"> |
||||||
|
Activité |
||||||
|
</h2> |
||||||
|
<div class="space-y-3 text-sm mb-4"> |
||||||
|
<div class="flex justify-between"> |
||||||
|
<span class="font-medium">Statut</span> |
||||||
|
<span class="text-green-400 font-bold">Running</span> |
||||||
|
</div> |
||||||
|
<div class="flex justify-between"> |
||||||
|
<span class="font-medium">Dernier lancement :</span> |
||||||
|
<span class="text-green-400 font-bold">2024-02-15 10:00</span> |
||||||
|
</div> |
||||||
|
<div class="flex justify-between"> |
||||||
|
<span class="font-medium">Dernier arrêt :</span> |
||||||
|
<span class="text-red-400 font-bold">2024-02-15 09:00</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="flex gap-3"> |
||||||
|
<a href="#" class="bg-green-500 hover:bg-green-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center gap-2"> |
||||||
|
<img src="/icons/start-icon.svg" alt="Start" class="w-5 h-5 invert"> |
||||||
|
</a> |
||||||
|
<a href="#" class="bg-red-500 hover:bg-red-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center gap-2"> |
||||||
|
<img src="/icons/stop-icon.svg" alt="Stop" class="w-5 h-5 invert"> |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- Carte : Hardware --> |
||||||
|
<div class="bg-gray-800 rounded-lg shadow-lg p-6"> |
||||||
|
<h2 class="text-2xl font-semibold border-b border-gray-700 pb-2 mb-4 flex items-center gap-2"> |
||||||
|
<img src="/icons/hardware-icon.svg" alt="Hardware" class="w-6 h-6 rotate-on-hover glow-on-hover invert"> |
||||||
|
Hardware |
||||||
|
</h2> |
||||||
|
<div class="space-y-3 text-sm"> |
||||||
|
<div class="flex justify-between"> |
||||||
|
<span class="font-medium">CPU :</span> |
||||||
|
<span>Intel Xeon E5</span> |
||||||
|
</div> |
||||||
|
<div class="flex justify-between"> |
||||||
|
<span class="font-medium">RAM :</span> |
||||||
|
<span>16GB</span> |
||||||
|
</div> |
||||||
|
<div class="flex justify-between"> |
||||||
|
<span class="font-medium">Stockage :</span> |
||||||
|
<span>10GB SSD</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
<!-- Section Actions en haut --> |
||||||
|
<section class="max-w-6xl mx-auto mb-6 fade-in"> |
||||||
|
<div class="flex justify-between items-center bg-gray-800 p-4 rounded-lg shadow-lg"> |
||||||
|
<div class="flex gap-3"> |
||||||
|
<button class="bg-blue-500 hover:bg-blue-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center"> |
||||||
|
<img src="/icons/share-icon.svg" alt="Partager" class="w-5 h-5 invert"> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
<!-- Actions de gestion --> |
||||||
|
<div class="flex gap-3"> |
||||||
|
<a href="#" class="bg-blue-500 hover:bg-blue-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center gap-2"> |
||||||
|
<img src="/icons/edit-icon.svg" alt="Edit" class="w-5 h-5 invert"> |
||||||
|
</a> |
||||||
|
<button class="bg-red-500 hover:bg-red-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center"> |
||||||
|
<img src="/icons/public-icon.svg" alt="Rendre public" class="w-5 h-5 invert"> |
||||||
|
</button> |
||||||
|
<a href="#" class="bg-red-500 hover:bg-red-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center gap-2"> |
||||||
|
<img src="/icons/delete-icon.svg" alt="Delete" class="w-5 h-5 invert"> |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</section> |
||||||
|
</Layout> |
||||||
|
</template> |
@ -1,8 +1,26 @@ |
|||||||
<?php |
<?php |
||||||
|
|
||||||
|
use App\Docker\Container; |
||||||
use Illuminate\Foundation\Inspiring; |
use Illuminate\Foundation\Inspiring; |
||||||
use Illuminate\Support\Facades\Artisan; |
use Illuminate\Support\Facades\Artisan; |
||||||
|
use Illuminate\Support\Facades\Log; |
||||||
|
|
||||||
Artisan::command('inspire', function () { |
Artisan::command('inspire', function () { |
||||||
$this->comment(Inspiring::quote()); |
$this->comment(Inspiring::quote()); |
||||||
})->purpose('Display an inspiring quote'); |
})->purpose('Display an inspiring quote'); |
||||||
|
|
||||||
|
Artisan::command('exec {id}', function ($id) { |
||||||
|
try{ |
||||||
|
$container = new Container($id); |
||||||
|
$exec = $container->exec(["apt install -y iproute2"]); |
||||||
|
Log::info(json_encode($exec->inspect(), JSON_PRETTY_PRINT)); |
||||||
|
$exec->start(); |
||||||
|
Log::info($container->logs()); |
||||||
|
$exec = $container->exec(["echo hello"]); |
||||||
|
$data = $exec->inspect(); |
||||||
|
Log::info(json_encode($data, JSON_PRETTY_PRINT)); |
||||||
|
$exec->start(); |
||||||
|
|
||||||
|
} catch (Exception $e) { Log::error(((object)$e)->getResponse()->getBody()); } |
||||||
|
|
||||||
|
})->purpose('Display an inspiring quote'); |
||||||
|
@ -1 +0,0 @@ |
|||||||
a:3:{s:6:"_token";s:40:"nwGLpFjKisR3R1Mu3mdHCaXL39V8ByW0CVFN2n2E";s:9:"_previous";a:1:{s:3:"url";s:27:"http://localhost:8000/spawn";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}} |
|
@ -0,0 +1,11 @@ |
|||||||
|
mkdir -p icons && cd icons |
||||||
|
|
||||||
|
# Télécharger les icônes (vérifie que ces URL correspondent bien aux fichiers SVG) |
||||||
|
wget -O server-icon.svg "https://www.svgrepo.com/download/452095/server.svg" |
||||||
|
wget -O security-icon.svg "https://www.svgrepo.com/download/488474/security-shield.svg" |
||||||
|
wget -O process-icon.svg "https://www.svgrepo.com/download/444524/process.svg" |
||||||
|
wget -O create-icon.svg "https://www.svgrepo.com/download/513806/add.svg" |
||||||
|
wget -O play-icon.svg "https://www.svgrepo.com/download/488502/play-button.svg" |
||||||
|
wget -O auto-icon.svg "https://www.svgrepo.com/download/456246/automation.svg" |
||||||
|
wget -O rocket-icon.svg "https://www.svgrepo.com/download/488478/rocket.svg" |
||||||
|
wget -O arrow-icon.svg "https://www.svgrepo.com/download/502744/arrow-right.svg" |
@ -0,0 +1,27 @@ |
|||||||
|
{ |
||||||
|
"Hostname": "mincraft-server", |
||||||
|
"User": "root", |
||||||
|
"Image": "minecraftbedrock:latest", |
||||||
|
"OpenStdin": true, |
||||||
|
"AttachStdout": false, |
||||||
|
"AttachStderr": false, |
||||||
|
"Tty": true, |
||||||
|
"ExposedPorts": { |
||||||
|
"19132/udp": {}, |
||||||
|
"19133/udp": {} |
||||||
|
}, |
||||||
|
"HostConfig": { |
||||||
|
"PortBindings": { |
||||||
|
"19132/udp": [ |
||||||
|
{ |
||||||
|
"HostPort": 25004 |
||||||
|
} |
||||||
|
], |
||||||
|
"19133/udp": [ |
||||||
|
{ |
||||||
|
"HostPort": 25005 |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
} |
||||||
|
} |