@ -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 |
||||
|
||||
use App\Docker\Container; |
||||
use Illuminate\Foundation\Inspiring; |
||||
use Illuminate\Support\Facades\Artisan; |
||||
use Illuminate\Support\Facades\Log; |
||||
|
||||
Artisan::command('inspire', function () { |
||||
$this->comment(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 |
||||
} |
||||
] |
||||
} |
||||
} |
||||
} |