Compare commits

..

5 Commits

Author SHA1 Message Date
anulax1225 6cd3411795 Dynamic et responsive comme je les aime ago%!(EXTRA string=2 months)
anulax1225 5f00d6188c Mon bebe ago%!(EXTRA string=2 months)
anulax1225 d522f986e7 Starting CRUD server ago%!(EXTRA string=2 months)
anulax1225 99a810c02b Starting CRUD server ago%!(EXTRA string=2 months)
anulax1225 662f2909f3 Interface up ago%!(EXTRA string=2 months)
  1. 30
      Servers/ArkServer/Dockerfile
  2. 4
      Servers/ArkServer/run.sh
  3. 18
      Servers/MinecraftBedrock/Dockerfile
  4. BIN
      Servers/MinecraftBedrock/bedrock-server-1.21.60.10.zip
  5. 2
      Servers/MinecraftJava/Dockerfile
  6. 34
      app/Console/Kernel.php
  7. 47
      app/Docker/Container.php
  8. 9
      app/Docker/Docker.php
  9. 56
      app/Docker/Exec.php
  10. 2
      app/Http/Controllers/Auth/AuthenticatedSessionController.php
  11. 2
      app/Http/Controllers/Auth/ConfirmablePasswordController.php
  12. 2
      app/Http/Controllers/Auth/EmailVerificationNotificationController.php
  13. 2
      app/Http/Controllers/Auth/EmailVerificationPromptController.php
  14. 2
      app/Http/Controllers/Auth/RegisteredUserController.php
  15. 4
      app/Http/Controllers/Auth/VerifyEmailController.php
  16. 179
      app/Http/Controllers/ServerController.php
  17. 25
      app/Http/Middleware/ResourceRules.php
  18. 66
      app/Jobs/InitServer.php
  19. 13
      app/Models/ExposedPort.php
  20. 55
      app/Models/Server.php
  21. 22
      app/Models/Service.php
  22. 15
      app/Models/Spec.php
  23. 13
      app/Models/Status.php
  24. 7
      app/Models/User.php
  25. 40
      app/Netstat.php
  26. 45
      config/docker.php
  27. 1
      database/migrations/0001_01_01_000000_create_users_table.php
  28. 32
      database/migrations/2025_02_14_231757_create_ports_table.php
  29. 62
      database/migrations/2025_02_14_231822_create_services_table.php
  30. 55
      database/migrations/2025_02_14_231846_create_server_statuses_table.php
  31. 54
      database/migrations/2025_02_14_232205_create_specs_table.php
  32. 40
      database/migrations/2025_02_14_234835_create_servers_table.php
  33. 32
      database/migrations/2025_02_16_230802_create_server_exposed_port.php
  34. 4
      public/icons/arrow-icon.svg
  35. 4
      public/icons/auto-icon.svg
  36. 5
      public/icons/copy-icon.svg
  37. 8
      public/icons/delete-icon.svg
  38. 11
      public/icons/details-icon.svg
  39. 4
      public/icons/edit-icon.svg
  40. 34
      public/icons/game-icon.svg
  41. 2
      public/icons/hardware-icon.svg
  42. 4
      public/icons/link-icon.svg
  43. 4
      public/icons/lock.svg
  44. 2
      public/icons/logout.svg
  45. 7
      public/icons/menu.svg
  46. 37
      public/icons/play-icon.svg
  47. 2
      public/icons/process-icon.svg
  48. 5
      public/icons/public-icon.svg
  49. 2
      public/icons/rocket-icon.svg
  50. 14
      public/icons/security-icon.svg
  51. 4
      public/icons/server-icon.svg
  52. 4
      public/icons/share-icon.svg
  53. 2
      public/icons/start-icon.svg
  54. 17
      public/icons/stats-icon.svg
  55. 4
      public/icons/status-icon.svg
  56. 4
      public/icons/stop-icon.svg
  57. 5
      public/icons/user.svg
  58. BIN
      public/img/ark.avif
  59. BIN
      public/img/ark.jpeg
  60. BIN
      public/img/banner.avif
  61. BIN
      public/img/banner.gif
  62. BIN
      public/img/banner.jpg
  63. BIN
      public/img/banner.webp
  64. BIN
      public/img/logo.png
  65. BIN
      public/img/minecraft-bedrock.webp
  66. BIN
      public/img/minecraft-java.webp
  67. 54
      resources/css/app.css
  68. 7
      resources/js/Components/NavLink.vue
  69. 8
      resources/js/Components/ResponsiveNavLink.vue
  70. 2
      resources/js/Components/TextInput.vue
  71. 6
      resources/js/Layouts/AuthenticatedLayout.vue
  72. 93
      resources/js/Layouts/Layout.vue
  73. 119
      resources/js/Pages/Auth/Login.vue
  74. 30
      resources/js/Pages/Dashboard.vue
  75. 68
      resources/js/Pages/Home.vue
  76. 42
      resources/js/Pages/Server/Create.vue
  77. 29
      resources/js/Pages/Server/Index.vue
  78. 73
      resources/js/Pages/Server/Paritals/ServerElement.vue
  79. 195
      resources/js/Pages/Server/Show.vue
  80. 2
      resources/views/app.blade.php
  81. 8
      routes/auth.php
  82. 30
      routes/console.php
  83. 55
      routes/web.php
  84. 2
      storage/framework/sessions/.gitignore
  85. 1
      storage/framework/sessions/jI3U5ZEiiHFkCvRoPtnLUtIXez0iMLTTjvzivOpi
  86. 11
      svg
  87. 2
      tests/Feature/Auth/AuthenticationTest.php
  88. 2
      tests/Feature/Auth/EmailVerificationTest.php
  89. 2
      tests/Feature/Auth/RegistrationTest.php
  90. 27
      text.json

@ -0,0 +1,30 @@
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
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" ]

@ -2,13 +2,11 @@ FROM ubuntu:latest
WORKDIR /app
RUN apt -y update
RUN apt -y upgrade
RUN apt install -y default-jre wget
RUN wget https://piston-data.mojang.com/v1/objects/4707d00eb834b446575d89a61a11b5d548d8c001/server.jar
RUN echo 'eula=true' > eula.txt
VOLUME [ "/app" ]
ENTRYPOINT ["java", "-Xmx2048M", "-Xms1024M", "-jar", "./server.jar", "nogui"]

@ -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');
}
}

@ -3,6 +3,7 @@
namespace App\Docker;
use Exception;
use Illuminate\Support\Facades\Log;
use Psr\Http\Message\ResponseInterface;
use React\Http\Browser;
use React\Socket\FixedUriConnector;
@ -30,7 +31,7 @@ public function start()
{
try {
$response = await($this->browser->post(Docker::endpoint("/containers/" . $this->id . "/start"), [
"Content-Type" => "text/plain"
"Content-Type" => "text/plain"
]));
return json_decode($response->getBody());
} catch (Exception $e) {
@ -38,9 +39,8 @@ public function start()
}
}
public function restart($context)
public function restart()
{
$context = $context ?? [ "onSuccess" => function() {}, "onError" => function() {}];
try {
$response = await($this->browser->post(
Docker::endpoint("/containers/" . $this->id . "/restart"),
@ -52,9 +52,8 @@ public function restart($context)
}
}
public function stop($context)
public function stop()
{
$context = $context ?? [ "onSuccess" => function() {}, "onError" => function() {}];
try {
$response = await($this->browser->post(
Docker::endpoint("/containers/" . $this->id . "/stop"),
@ -66,9 +65,8 @@ public function stop($context)
}
}
public function kill($context)
public function kill()
{
$context = $context ?? [ "onSuccess" => function() {}, "onError" => function() {}];
try {
$response = await($this->browser->post(
Docker::endpoint("/containers/" . $this->id . "/kill"),
@ -83,7 +81,7 @@ public function kill($context)
public function inspect($size = false)
{
try {
$response = await($this->browser->post(
$response = await($this->browser->get(
Docker::endpoint("/containers/" . $this->id . "/json?size=" . $size),
[ "Content-Type" => "text/plain" ]
));
@ -93,13 +91,44 @@ public function inspect($size = false)
}
}
public function logs($args = [])
{
try {
$response = await($this->browser->requestStreaming("GET",
Docker::endpoint("/containers/" . $this->id . "/logs?stdout=true?stderr=true"),
[ "Content-Type" => "text/plain" ]
));
return $response->getBody();
} catch (Exception $e) {
throw $e;
}
}
public function exec($command)
{
try {
$response = await($this->browser->post(
Docker::endpoint("/containers/" . $this->id . "/exec"),
[ "Content-Type" => "application/json" ],
json_encode([
'AttachStdout' => true,
'AttachStderr' => true,
'Cmd' => $command
])
));
return new Exec(json_decode($response->getBody())->Id);
} catch (Exception $e) {
throw $e;
}
}
public static function create($name, $config)
{
try {
$connection = Docker::connect();
$response = await($connection->browser->post(Docker::endpoint('/containers/create?name='. $name),
[ "Content-Type" => "application/json" ],
json_encode($config)
json_encode($config, JSON_UNESCAPED_SLASHES)
));
$data = json_decode($response->getBody());
$container = new Container($data->Id);

@ -6,21 +6,24 @@
use Psr\Http\Message\ResponseInterface;
use React\Http\Browser;
use React\Socket\FixedUriConnector;
use React\Socket\ConnectorInterFace;
use React\Socket\Connector;
use React\Socket\UnixConnector;
use function React\Async\await;
class Docker
{
static protected $connection = null;
public static function connect($fromSocket = true, $socket = 'unix:///var/run/docker.sock')
{
$connector = $fromSocket ? new FixedUriConnector(
$socket,
new UnixConnector()
) : new ConnectorInterface();
) : null;
$browser = new Browser($connector);
return (object)[ "connector" => $connector, "browser" => $browser ];
Docker::$connection = (object)[ "connector" => $connector, "browser" => $browser ];
return Docker::$connection;
}
public static function endpoint($url)

@ -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;
}
}
}

@ -33,7 +33,7 @@ public function store(LoginRequest $request): RedirectResponse
$request->session()->regenerate();
return redirect()->intended(route('dashboard', absolute: false));
return redirect()->intended(route('home', absolute: false));
}
/**

@ -36,6 +36,6 @@ public function store(Request $request): RedirectResponse
$request->session()->put('auth.password_confirmed_at', time());
return redirect()->intended(route('dashboard', absolute: false));
return redirect()->intended(route('home', absolute: false));
}
}

@ -14,7 +14,7 @@ class EmailVerificationNotificationController extends Controller
public function store(Request $request): RedirectResponse
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(route('dashboard', absolute: false));
return redirect()->intended(route('home', absolute: false));
}
$request->user()->sendEmailVerificationNotification();

@ -16,7 +16,7 @@ class EmailVerificationPromptController extends Controller
public function __invoke(Request $request): RedirectResponse|Response
{
return $request->user()->hasVerifiedEmail()
? redirect()->intended(route('dashboard', absolute: false))
? redirect()->intended(route('home', absolute: false))
: Inertia::render('Auth/VerifyEmail', ['status' => session('status')]);
}
}

@ -46,6 +46,6 @@ public function store(Request $request): RedirectResponse
Auth::login($user);
return redirect(route('dashboard', absolute: false));
return redirect(route('home', absolute: false));
}
}

@ -15,13 +15,13 @@ class VerifyEmailController extends Controller
public function __invoke(EmailVerificationRequest $request): RedirectResponse
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
return redirect()->intended(route('home', absolute: false).'?verified=1');
}
if ($request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
return redirect()->intended(route('home', absolute: false).'?verified=1');
}
}

@ -0,0 +1,179 @@
<?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 Carbon\Carbon;
use Exception;
use Illuminate\Support\Facades\Auth;
use Inertia\Inertia;
class ServerController extends Controller
{
public function start(Request $request)
{
$server = Server::where("uuid", $request->id)->first();
if($server->status_id >= 3) {
try {
$container = new Container($server->container);
$container->start();
$server->update([
"status_id" => 1,
"start" => Carbon::now()
]);
return redirect()->back()->withErrors([ "message" => "Serveur lancé avec succès. Attendez quelque seconds avant de vous connectez." ]);
} catch(Exception $e) { return redirect()->back()->withErrors([ "error" => "Impossible de démarré le serveur." ]); }
} else { return redirect()->back()->withErrors([ "error" => "Serveur déjà ou entrain de démarré." ]); }
}
public function stop(Request $request)
{
$server = Server::where("uuid", $request->id)->first();
if($server->status_id < 3) {
try {
$server->update([
"status_id" => 3,
"end" => Carbon::now()
]);
$container = new Container($server->container);
$container->stop();
return redirect()->back()->withErrors([ "message" => "Serveur arrêté avec succès. Merci de votre visite." ]);
} catch(Exception $e) { return redirect()->back()->withErrors([ "error" => "Impossible de d'arrêter le serveur." ]); }
} else { return redirect()->back()->withErrors([ "error" => "Serveur déjà ou entrain de s'arrêter." ]); }
}
/**
* Display a listing of the resource.
*/
public function index()
{
return Inertia::render("Server/Index", [
"banner" => "/img/banner.gif",
"servers" => (Auth::user()->admin ? Server::orderBy("start", "DESC")->orderBy("end", "DESC")->get() : Server::where("user_id", Auth::user()->id)->get())->jsonSerialize(),
]);
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
return Inertia::render('Server/Create', [
"banner" => "/img/banner.jpg",
"services" => Service::all()
]);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$request->validate([
"name" => "required|string",
"service" => "required|string",
"launch" => "nullable"
]);
$user = Auth::user();
if(!$user->admin && count($user->server) >= 2) return redirect()->back()->withErrors(["error" => "Vous avez déjà trop de servers. Veillez en supprimer un!"]);
$service = Service::where("uuid", $request->service)->first();
if(!$service) return redirect()->back()->withErrors(["error" => "Impossible de trouver ce service!"]);
$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
]);
$config = config($server->service->config);
$ports = explode("|", $server->service->ports);
for ($i = 0; $i < count($ports); $i++) {
$port = ExposedPort::where("usable", true)->first();
if(!$port) {
$server->exposedPorts()->update([ "usable" => true ]);
$server->exposedPorts()->detach();
$server->delete();
return redirect()->back()->withErrors(["error" => "Aucune place libre pour votre server, réessayer plus tard."]);
}
$server->exposedPorts()->attach($port->id);
$port->update(["usable" => false]);
$config["ExposedPorts"][$ports[$i] . "/" . $server->service->protocol] = (object)[];
$config["HostConfig"]["PortBindings"][$ports[$i] . "/" . $server->service->protocol] = [[ "HostPort" => "" . $port->number ]];
}
dispatch(new InitServer($server, $config, $request->launch ? true : false));
return redirect(route("servers.index"))->withErrors([ "message" => "Serveur créer avec succès" . ($request->launch ? " et devrait se lancer d'une minute à l'autre." : ".") ]);
}
/**
* Display the specified resource.
*/
public function show(Request $request)
{
$server = Server::where("uuid", $request->id)->firstOrFail();
return Inertia::render("Server/Show", [
"banner" => "/img/banner.webp",
"server" => $server->jsonSerialize()
]);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request)
{
$request->validate([
"name" => "required|string|max:25",
]);
$server = Server::where("uuid", $request->id)->first();
$server->update([
"name" => preg_replace('/[^a-zA-Z0-9_.-]/', '', $request->name)
]);
return redirect()->back()->withErrors(["message" => "Nom changé avec succès."]);
}
public function makePublic(Request $request)
{
$server = Server::where("uuid", $request->id)->first();
$server->update([
"public" => !$server->public,
]);
return redirect()->back()->withErrors(["message" => $server->public ? "Server rendu publiquement accéssible." : "Server rendu privé."]);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Request $request)
{
$server = Server::where("uuid", $request->id)->first();
if($server->container) {
try {
$container = new Container($server->container);
$data = $container->inspect()->State;
if($data->Running || $data->Paused || $data->Restarting) $container->kill();
} catch(Exception) { return redirect()->back()->withErrors(["error" => "Problème pour éteindre le serveur. Réessayer!"]); }
}
$server->exposedPorts()->update([ "usable" => true ]);
$server->exposedPorts()->detach();
$server->delete();
return redirect(route("servers.index"))->withErrors(["message" => "Server supprimer avec succès."]);
}
}

@ -0,0 +1,25 @@
<?php
namespace App\Http\Middleware;
use App\Models\Server;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class ResourceRules
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$server = Server::where("uuid", $request->id)->first();
if(!$server) return redirect(route("servers.index"))->withErrors(["error" => "Impossible de trouver ce server!"]);
if(Auth::user()->id == $server->id || Auth::user()->admin) return $next($request);
return redirect(route("servers.index"))->withErrors(["error" => "Vous n'avez d'autorisation d'access a se serveur!"]);
}
}

@ -0,0 +1,66 @@
<?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;
protected $config;
protected $launch;
/**
* Create a new job instance.
*/
public function __construct($server, $config, $launch)
{
$this->server = $server;
$this->launch = $launch;
$this->config = $config;
$server->update(["status_id" => 2]);
}
/**
* Execute the job.
*/
public function handle(): void
{
try {
$container = Container::create($this->server->name . Str::random(5), $this->config);
if($this->launch) {
$container->start();
$this->server->update([
"container" => $container->getId(),
"status_id" => 1,
"start" => now(),
"end" => now()
]);
} else {
$this->server->update([
"container" => $container->getId(),
"status_id" => 3,
"end" => now()
]);
}
}catch(Exception $e) {
$this->fail($e);
}
}
public function fail($e): void
{
$this->server->update([
"status_id" => 4,
]);
$this->server->exposedPorts()->detach();
Log::info(((object)$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,55 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Server extends Model
{
protected $fillable = [
"uuid",
'name',
"container",
"start",
"end",
"public",
"service_id",
"user_id",
"status_id",
];
public function jsonSerialize(): mixed
{
return [
"uuid" => $this->uuid,
'name' => $this->name,
"start" => $this->start,
"end" => $this->end,
"public" => $this->public,
"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'
];
}

@ -21,8 +21,15 @@ class User extends Authenticatable
'name',
'email',
'password',
'email_verified_at',
'admin'
];
public function servers()
{
return $this->hasMany(Server::class);
}
/**
* The attributes that should be hidden for serialization.
*

@ -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" => [],
]
]
];

@ -14,6 +14,7 @@ public function up(): void
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->boolean("admin")->default(false);
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');

@ -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,55 @@
<?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" => "Stopping",
"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,40 @@
<?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->boolean("public")->default(false);
$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');
}
};

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11 9L8 12M8 12L11 15M8 12H16M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 463 B

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<path d="M144.282 145.51A32.19 32.19 0 0 1 150 145c4.032 0 7.89.746 11.444 2.107L175.584 127l.54.306A31.88 31.88 0 0 1 168 106c0-17.673 14.327-32 32-32 17.673 0 32 14.327 32 32 0 17.673-14.327 32-32 32-3.672 0-7.2-.619-10.485-1.757l-14.31 21.038A31.863 31.863 0 0 1 182 177c0 17.673-14.327 32-32 32-17.673 0-32-14.327-32-32 0-9.767 4.376-18.512 11.274-24.382l-20.764-41.28A32.14 32.14 0 0 1 102 112a32.05 32.05 0 0 1-8.16-1.05l-14.716 25.93C85.21 142.705 89 150.91 89 160c0 17.673-14.327 32-32 32-17.673 0-32-14.327-32-32 0-17.673 14.327-32 32-32 2.655 0 5.234.323 7.7.932l14.809-26.17C73.638 96.963 70 88.907 70 80c0-17.673 14.327-32 32-32 17.673 0 32 14.327 32 32 0 9.563-4.195 18.146-10.844 24.01l21.126 41.5zM200 122c8.837 0 16-7.163 16-16s-7.163-16-16-16-16 7.163-16 16 7.163 16 16 16zM57 176c8.837 0 16-7.163 16-16s-7.163-16-16-16-16 7.163-16 16 7.163 16 16 16zm45-80c8.837 0 16-7.163 16-16s-7.163-16-16-16-16 7.163-16 16 7.163 16 16 16zm48 97c8.837 0 16-7.163 16-16s-7.163-16-16-16-16 7.163-16 16 7.163 16 16 16z" fill-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 -0.5 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.676 14.248C17.676 15.8651 16.3651 17.176 14.748 17.176H7.428C5.81091 17.176 4.5 15.8651 4.5 14.248V6.928C4.5 5.31091 5.81091 4 7.428 4H14.748C16.3651 4 17.676 5.31091 17.676 6.928V14.248Z" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.252 20H17.572C19.1891 20 20.5 18.689 20.5 17.072V9.75195" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 713 B

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 11V17" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 11V17" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4 7H20" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 7H12H18V18C18 19.6569 16.6569 21 15 21H9C7.34315 21 6 19.6569 6 18V7Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 5C9 3.89543 9.89543 3 11 3H13C14.1046 3 15 3.89543 15 5V7H9V5Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 859 B

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Menu / More_Vertical">
<g id="Vector">
<path d="M11 18C11 18.5523 11.4477 19 12 19C12.5523 19 13 18.5523 13 18C13 17.4477 12.5523 17 12 17C11.4477 17 11 17.4477 11 18Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 12C11 12.5523 11.4477 13 12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 6C11 6.55228 11.4477 7 12 7C12.5523 7 13 6.55228 13 6C13 5.44772 12.5523 5 12 5C11.4477 5 11 5.44772 11 6Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 924 B

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.8477 1.87868C19.6761 0.707109 17.7766 0.707105 16.605 1.87868L2.44744 16.0363C2.02864 16.4551 1.74317 16.9885 1.62702 17.5692L1.03995 20.5046C0.760062 21.904 1.9939 23.1379 3.39334 22.858L6.32868 22.2709C6.90945 22.1548 7.44285 21.8693 7.86165 21.4505L22.0192 7.29289C23.1908 6.12132 23.1908 4.22183 22.0192 3.05025L20.8477 1.87868ZM18.0192 3.29289C18.4098 2.90237 19.0429 2.90237 19.4335 3.29289L20.605 4.46447C20.9956 4.85499 20.9956 5.48815 20.605 5.87868L17.9334 8.55027L15.3477 5.96448L18.0192 3.29289ZM13.9334 7.3787L3.86165 17.4505C3.72205 17.5901 3.6269 17.7679 3.58818 17.9615L3.00111 20.8968L5.93645 20.3097C6.13004 20.271 6.30784 20.1759 6.44744 20.0363L16.5192 9.96448L13.9334 7.3787Z" fill="#0F0F0F"/>
</svg>

After

Width:  |  Height:  |  Size: 995 B

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg height="800px" width="800px" version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512 512" xml:space="preserve">
<style type="text/css">
.st0{fill:#000000;}
</style>
<g>
<path class="st0" d="M510.002,309.835l-0.068-0.326l-0.076-0.334l-26.508-112.721l-0.106-0.417l-0.106-0.418
c-16.668-62.217-73.294-105.666-137.712-105.666H166.579c-64.418,0-121.045,43.449-137.712,105.666l-0.114,0.418l-0.099,0.417
L2.147,309.174l-0.076,0.326l-0.068,0.326c-9.749,46.43,16.926,92.496,62.036,107.168l1.586,0.509
c9.24,3.012,18.89,4.544,28.624,4.544c32.668,0,63.128-17.404,79.758-45.489l22.556-33.343l0.561-0.835l0.509-0.872
c0.796-1.388,2.276-2.253,3.861-2.253h109.02c1.586,0,3.066,0.865,3.862,2.253l0.508,0.872l0.562,0.835l22.555,33.343
c16.63,28.085,47.09,45.489,79.766,45.489c9.734,0,19.384-1.532,28.67-4.56l1.533-0.493
C493.07,402.331,519.737,356.257,510.002,309.835z M439.318,390.397l-1.54,0.501c-6.608,2.154-13.353,3.186-20.014,3.186
c-22.646,0-44.283-11.949-56.088-32.433l-23.064-34.101c-5.788-10.053-16.508-16.258-28.101-16.258h-109.02
c-11.592,0-22.312,6.206-28.101,16.258l-23.063,34.101c-11.804,20.484-33.434,32.433-56.081,32.433
c-6.661,0-13.405-1.032-20.013-3.186l-1.548-0.501c-31.431-10.219-50.102-42.485-43.311-74.819l26.508-112.722
c13.42-50.102,58.826-84.94,110.696-84.94h178.847c51.869,0,97.276,34.838,110.696,84.94l26.508,112.722
C489.413,347.912,470.75,380.178,439.318,390.397z"/>
<polygon class="st0" points="157.453,172.061 123.912,172.061 123.912,210.579 85.387,210.579 85.387,244.105 123.912,244.105
123.912,282.637 157.453,282.637 157.453,244.105 195.978,244.105 195.978,210.579 157.453,210.579 "/>
<path class="st0" d="M365.721,206.247c11.668,0,21.113-9.445,21.113-21.098c0-11.669-9.445-21.114-21.113-21.114
c-11.653,0-21.098,9.445-21.098,21.114C344.622,196.802,354.068,206.247,365.721,206.247z"/>
<path class="st0" d="M323.509,206.247c-11.653,0-21.106,9.453-21.106,21.098c0,11.669,9.453,21.122,21.106,21.122
c11.661,0,21.106-9.453,21.106-21.122C344.615,215.7,335.17,206.247,323.509,206.247z"/>
<path class="st0" d="M365.721,248.459c-11.653,0-21.098,9.445-21.098,21.114c0,11.653,9.445,21.098,21.098,21.098
c11.668,0,21.113-9.445,21.113-21.098C386.834,257.904,377.388,248.459,365.721,248.459z"/>
<path class="st0" d="M407.933,206.247c-11.653,0-21.099,9.453-21.099,21.098c0,11.669,9.446,21.122,21.099,21.122
c11.66,0,21.113-9.453,21.113-21.122C429.046,215.7,419.593,206.247,407.933,206.247z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><title>ionicons-v5-l</title><rect x="80" y="80" width="352" height="352" rx="48" ry="48" style="fill:none;stroke:#000000;stroke-linejoin:round;stroke-width:32px"/><rect x="144" y="144" width="224" height="224" rx="16" ry="16" style="fill:none;stroke:#000000;stroke-linejoin:round;stroke-width:32px"/><line x1="256" y1="80" x2="256" y2="48" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="336" y1="80" x2="336" y2="48" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="176" y1="80" x2="176" y2="48" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="256" y1="464" x2="256" y2="432" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="336" y1="464" x2="336" y2="432" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="176" y1="464" x2="176" y2="432" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="432" y1="256" x2="464" y2="256" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="432" y1="336" x2="464" y2="336" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="432" y1="176" x2="464" y2="176" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="48" y1="256" x2="80" y2="256" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="48" y1="336" x2="80" y2="336" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="48" y1="176" x2="80" y2="176" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 1920 1920" xmlns="http://www.w3.org/2000/svg">
<path d="m1311.326 891.546-61.666-61.666 107.294-107.407-159.698-159.698-107.52 107.633-61.553-60.311 384.338-384.904 282.127 281.45-383.322 384.903ZM891.863 1309.43 508.54 1694.332l-283.143-281.45 384.339-384.903 61.214 61.214-110.005 109.892 159.699 159.699 110.117-110.005 61.102 60.65Zm962.484-961.92-283.03-283.03c-88.546-86.288-231.19-85.95-317.93 1.016L868.484 450.398c-87.53 87.642-87.53 230.061 0 317.704l61.666 61.778-99.502 99.502-60.31-60.198c-42.127-43.144-98.372-67.087-158.57-67.426h-1.355c-60.197 0-116.894 23.605-159.812 66.41L65.698 1253.184c-87.642 87.642-87.642 230.061 0 317.704L348.84 1854.03c43.822 43.821 101.309 65.732 158.909 65.732 57.487 0 115.087-21.91 158.908-65.732l384.903-384.903c43.257-43.257 66.862-100.518 66.523-161.168-.452-60.197-24.396-116.442-66.523-157.665l-61.214-61.102 99.614-99.614 61.666 61.78c43.821 43.707 101.309 65.618 158.909 65.618 57.487 0 115.087-21.91 158.795-65.732l385.016-384.903c86.852-86.852 87.304-229.497 0-318.833Z" fill-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 14.5V16.5M7 10.0288C7.47142 10 8.05259 10 8.8 10H15.2C15.9474 10 16.5286 10 17 10.0288M7 10.0288C6.41168 10.0647 5.99429 10.1455 5.63803 10.327C5.07354 10.6146 4.6146 11.0735 4.32698 11.638C4 12.2798 4 13.1198 4 14.8V16.2C4 17.8802 4 18.7202 4.32698 19.362C4.6146 19.9265 5.07354 20.3854 5.63803 20.673C6.27976 21 7.11984 21 8.8 21H15.2C16.8802 21 17.7202 21 18.362 20.673C18.9265 20.3854 19.3854 19.9265 19.673 19.362C20 18.7202 20 17.8802 20 16.2V14.8C20 13.1198 20 12.2798 19.673 11.638C19.3854 11.0735 18.9265 10.6146 18.362 10.327C18.0057 10.1455 17.5883 10.0647 17 10.0288M7 10.0288V8C7 5.23858 9.23858 3 12 3C14.7614 3 17 5.23858 17 8V10.0288" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 974 B

@ -0,0 +1,2 @@
<?xml version="1.0" ?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15 3H7C5.89543 3 5 3.89543 5 5V19C5 20.1046 5.89543 21 7 21H15" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M19 12L15 8M19 12L15 16M19 12H9" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></svg>

After

Width:  |  Height:  |  Size: 490 B

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Menu / Menu_Alt_01">
<path id="Vector" d="M12 17H19M5 12H19M5 7H19" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 397 B

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg height="800px" width="800px" version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512 512" xml:space="preserve">
<style type="text/css">
.st0{fill:#000000;}
</style>
<g>
<path class="st0" d="M389.488,226.899H122.512C54.854,226.899,0,281.746,0,349.412c0,67.658,54.854,122.512,122.512,122.512
c42.653,0,80.199-21.812,102.135-54.854h62.706c21.936,33.042,59.49,54.854,102.135,54.854C457.154,471.924,512,417.07,512,349.412
C512,281.746,457.154,226.899,389.488,226.899z M389.488,440.332c-30.535,0-58.881-15.226-75.818-40.74l-9.372-14.115h-16.945
h-62.706h-16.945l-9.371,14.115c-16.938,25.514-45.282,40.74-75.819,40.74c-50.134,0-90.92-40.786-90.92-90.92
c0-50.134,40.786-90.921,90.92-90.921h266.976c50.134,0,90.92,40.786,90.92,90.921
C480.408,399.545,439.622,440.332,389.488,440.332z"/>
<path class="st0" d="M268.927,110.897c0-2.46,0.486-4.72,1.366-6.803c1.311-3.116,3.548-5.8,6.332-7.69
c2.8-1.89,6.085-2.97,9.757-2.97c2.437,0,4.705,0.494,6.788,1.373c3.116,1.311,5.799,3.533,7.689,6.333
c1.882,2.8,2.97,6.085,2.97,9.756c0,5.893,1.203,11.593,3.394,16.752c3.278,7.744,8.716,14.292,15.58,18.928
c6.849,4.643,15.21,7.374,24.08,7.366c5.916,0,11.616-1.211,16.768-3.394c7.728-3.278,14.284-8.716,18.92-15.58
c4.643-6.857,7.366-15.21,7.366-24.072v-70.82h-25.607v70.82c0,2.437-0.478,4.705-1.357,6.78c-1.319,3.124-3.556,5.808-6.333,7.69
c-2.808,1.881-6.093,2.969-9.757,2.969c-2.436,0-4.705-0.486-6.787-1.365c-3.116-1.311-5.8-3.548-7.682-6.333
c-1.889-2.8-2.977-6.1-2.992-9.741c0.016-5.924-1.188-11.616-3.378-16.768c-3.279-7.744-8.716-14.292-15.58-18.928
c-6.864-4.651-15.21-7.374-24.08-7.366c-5.909-0.008-11.608,1.203-16.768,3.394c-7.743,3.263-14.292,8.716-18.927,15.58
c-4.644,6.857-7.359,15.21-7.359,24.088v104.71h25.599V110.897z"/>
<polygon class="st0" points="138.363,293.338 105.452,293.338 105.452,331.124 67.658,331.124 67.658,364.051 105.452,364.051
105.452,401.829 138.363,401.829 138.363,364.051 176.156,364.051 176.156,331.124 138.363,331.124 "/>
<path class="st0" d="M375.126,289.698c-7.937,7.929-7.921,20.794,0,28.723c7.936,7.929,20.794,7.929,28.716,0
c7.936-7.929,7.936-20.794,0-28.723C395.92,281.769,383.063,281.769,375.126,289.698z"/>
<path class="st0" d="M329.774,335.05c-7.929,7.929-7.929,20.794,0,28.723c7.937,7.929,20.794,7.929,28.723,0
c7.93-7.929,7.93-20.794,0-28.731C350.568,327.121,337.711,327.121,329.774,335.05z"/>
<path class="st0" d="M375.126,380.402c-7.937,7.929-7.937,20.786,0,28.715c7.936,7.929,20.794,7.929,28.716,0.008
c7.936-7.937,7.936-20.794,0-28.739C395.92,372.457,383.063,372.473,375.126,380.402z"/>
<path class="st0" d="M449.193,335.05c-7.921-7.929-20.786-7.929-28.723,0c-7.937,7.929-7.937,20.794,0,28.723
c7.937,7.929,20.802,7.929,28.723,0C457.123,355.844,457.123,342.979,449.193,335.05z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24"><path d="M8.5,6H6.7C8.2,4.7,10,4,12,4c0.3,0,0.6,0,0.9,0.1c0,0,0,0,0,0c0.5,0.1,1-0.3,1.1-0.9c0.1-0.5-0.3-1-0.9-1.1C12.7,2,12.4,2,12,2C9.6,2,7.3,2.9,5.5,4.4V3c0-0.6-0.4-1-1-1s-1,0.4-1,1v4c0,0.6,0.4,1,1,1h4c0.6,0,1-0.4,1-1S9.1,6,8.5,6z M7,14.5c-0.6,0-1,0.4-1,1v1.8C4.7,15.8,4,14,4,12c0-0.3,0-0.6,0.1-0.9c0,0,0,0,0,0c0.1-0.5-0.3-1-0.9-1.1c-0.5-0.1-1,0.3-1.1,0.9C2,11.3,2,11.6,2,12c0,2.4,0.9,4.7,2.4,6.5H3c-0.6,0-1,0.4-1,1s0.4,1,1,1h4c0.3,0,0.6-0.2,0.8-0.4c0,0,0,0,0,0c0,0,0,0,0,0c0-0.1,0.1-0.2,0.1-0.3c0-0.1,0-0.1,0-0.2c0,0,0-0.1,0-0.1v-4C8,14.9,7.6,14.5,7,14.5z M21,5.5c0.6,0,1-0.4,1-1s-0.4-1-1-1h-4c-0.1,0-0.1,0-0.2,0c0,0,0,0,0,0c-0.1,0-0.2,0.1-0.3,0.1c0,0,0,0,0,0c-0.1,0.1-0.2,0.1-0.2,0.2c0,0,0,0,0,0c0,0,0,0,0,0c0,0.1-0.1,0.2-0.1,0.2c0,0.1,0,0.1,0,0.2c0,0,0,0.1,0,0.1v4c0,0.6,0.4,1,1,1s1-0.4,1-1V6.7c1.3,1.4,2,3.3,2,5.3c0,0.3,0,0.6-0.1,0.9c-0.1,0.5,0.3,1,0.9,1.1c0,0,0.1,0,0.1,0c0.5,0,0.9-0.4,1-0.9c0-0.4,0.1-0.7,0.1-1.1c0-2.4-0.9-4.7-2.4-6.5H21z M20.3,16.5c-0.1-0.1-0.2-0.2-0.3-0.3c0,0,0,0,0,0c0,0,0,0,0,0c-0.1-0.1-0.2-0.1-0.3-0.1c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1,0h-4c-0.6,0-1,0.4-1,1s0.4,1,1,1h1.8c-1.4,1.3-3.3,2-5.3,2c-0.3,0-0.6,0-0.9-0.1c0,0,0,0,0,0c-0.5-0.1-1,0.3-1.1,0.9s0.3,1,0.9,1.1c0.4,0,0.7,0.1,1.1,0.1c2.4,0,4.7-0.9,6.5-2.4V21c0,0.6,0.4,1,1,1s1-0.4,1-1v-4C20.5,16.8,20.4,16.6,20.3,16.5C20.3,16.5,20.3,16.5,20.3,16.5z"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.5 16.5854C13.5 17.4138 12.8284 18.0854 12 18.0854C11.1716 18.0854 10.5 17.4138 10.5 16.5854C10.5 15.7569 11.1716 15.0854 12 15.0854C12.8284 15.0854 13.5 15.7569 13.5 16.5854Z" fill="#0F0F0F"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.33367 10C6.20971 9.64407 6.09518 9.27081 5.99836 8.88671C5.69532 7.68444 5.54485 6.29432 5.89748 4.97439C6.26228 3.60888 7.14664 2.39739 8.74323 1.59523C10.3398 0.793061 11.8397 0.806642 13.153 1.32902C14.4225 1.83396 15.448 2.78443 16.2317 3.7452C16.4302 3.98851 16.6166 4.23669 16.7907 4.48449C17.0806 4.89706 16.9784 5.45918 16.5823 5.7713C16.112 6.14195 15.4266 6.01135 15.0768 5.52533C14.9514 5.35112 14.8197 5.17831 14.6819 5.0094C14.0088 4.18414 13.2423 3.51693 12.4138 3.18741C11.6292 2.87533 10.7252 2.83767 9.64112 3.38234C8.55703 3.92702 8.04765 4.6748 7.82971 5.49059C7.5996 6.35195 7.6774 7.36518 7.93771 8.39788C8.07953 8.96054 8.26936 9.50489 8.47135 10H18C19.6569 10 21 11.3431 21 13V20C21 21.6569 19.6569 23 18 23H6C4.34315 23 3 21.6569 3 20V13C3 11.3431 4.34315 10 6 10H6.33367ZM19 13C19 12.4477 18.5523 12 18 12H6C5.44772 12 5 12.4477 5 13V20C5 20.5523 5.44772 21 6 21H18C18.5523 21 19 20.5523 19 20V13Z" fill="#0F0F0F"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg"><defs><style>.cls-1{fill:none;stroke:#020202;stroke-miterlimit:10;stroke-width:1.91px;}</style></defs><path class="cls-1" d="M13.11,16.55,7.45,10.89l7.27-7.27a7.24,7.24,0,0,1,7.41-1.75h0a7.24,7.24,0,0,1-1.75,7.41Z"/><ellipse class="cls-1" cx="14.85" cy="9.09" rx="0.94" ry="0.91"/><polygon class="cls-1" points="9.57 17.26 6.74 14.43 8.16 11.6 12.4 15.84 9.57 17.26"/><path class="cls-1" d="M12.88,14.11h0a4,4,0,0,1,4,4v3a0,0,0,0,1,0,0h0a4,4,0,0,1-4-4v-3A0,0,0,0,1,12.88,14.11Z" transform="translate(16.81 -5.36) rotate(45)"/><path class="cls-1" d="M8.39,5.62h0a0,0,0,0,1,0,0v3a4,4,0,0,1-4,4h0a0,0,0,0,1,0,0v-3a4,4,0,0,1,4-4Z" transform="translate(4.46 20.1) rotate(-135)"/><path class="cls-1" d="M3.73,16.27A3,3,0,0,1,8,16a3,3,0,0,1-.27,4.27C5.45,22.55,1.5,22.5,1.5,22.5S1.45,18.55,3.73,16.27Z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<title>security-verified</title>
<g id="Layer_2" data-name="Layer 2">
<g id="invisible_box" data-name="invisible box">
<rect width="48" height="48" fill="none"/>
</g>
<g id="icons_Q2" data-name="icons Q2">
<path d="M24,6.2c5.3,1.5,11.1,3.3,14,4.3V26.2c0,3.4-3.7,9.4-14,15.4-10.3-6.1-14-12-14-15.4V10.5c2.9-1.1,8.7-2.8,14-4.3M24,2S6,7.1,6,8V26.2c0,9.2,13.3,17.3,17,19.5a1.8,1.8,0,0,0,2,0c3.8-2.1,17-10.3,17-19.5V8c0-.9-18-6-18-6Z"/>
<path d="M19.6,29.4l-5-4.9a2.1,2.1,0,0,1-.2-2.7,1.9,1.9,0,0,1,3-.2L21,25.2l9.6-9.6a2,2,0,0,1,2.8,2.8l-11,11A1.9,1.9,0,0,1,19.6,29.4Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 846 B

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 7H18.01M15 7H15.01M18 17H18.01M15 17H15.01M6 10H18C18.9319 10 19.3978 10 19.7654 9.84776C20.2554 9.64477 20.6448 9.25542 20.8478 8.76537C21 8.39782 21 7.93188 21 7C21 6.06812 21 5.60218 20.8478 5.23463C20.6448 4.74458 20.2554 4.35523 19.7654 4.15224C19.3978 4 18.9319 4 18 4H6C5.06812 4 4.60218 4 4.23463 4.15224C3.74458 4.35523 3.35523 4.74458 3.15224 5.23463C3 5.60218 3 6.06812 3 7C3 7.93188 3 8.39782 3.15224 8.76537C3.35523 9.25542 3.74458 9.64477 4.23463 9.84776C4.60218 10 5.06812 10 6 10ZM6 20H18C18.9319 20 19.3978 20 19.7654 19.8478C20.2554 19.6448 20.6448 19.2554 20.8478 18.7654C21 18.3978 21 17.9319 21 17C21 16.0681 21 15.6022 20.8478 15.2346C20.6448 14.7446 20.2554 14.3552 19.7654 14.1522C19.3978 14 18.9319 14 18 14H6C5.06812 14 4.60218 14 4.23463 14.1522C3.74458 14.3552 3.35523 14.7446 3.15224 15.2346C3 15.6022 3 16.0681 3 17C3 17.9319 3 18.3978 3.15224 18.7654C3.35523 19.2554 3.74458 19.6448 4.23463 19.8478C4.60218 20 5.06812 20 6 20Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M23 5.5C23 7.98528 20.9853 10 18.5 10C17.0993 10 15.8481 9.36007 15.0228 8.35663L9.87308 10.9315C9.95603 11.2731 10 11.63 10 11.9971C10 12.3661 9.9556 12.7247 9.87184 13.0678L15.0228 15.6433C15.8482 14.6399 17.0993 14 18.5 14C20.9853 14 23 16.0147 23 18.5C23 20.9853 20.9853 23 18.5 23C16.0147 23 14 20.9853 14 18.5C14 18.1319 14.0442 17.7742 14.1276 17.4318L8.97554 14.8558C8.1502 15.8581 6.89973 16.4971 5.5 16.4971C3.01472 16.4971 1 14.4824 1 11.9971C1 9.51185 3.01472 7.49713 5.5 7.49713C6.90161 7.49713 8.15356 8.13793 8.97886 9.14254L14.1275 6.5682C14.0442 6.2258 14 5.86806 14 5.5C14 3.01472 16.0147 1 18.5 1C20.9853 1 23 3.01472 23 5.5ZM16.0029 5.5C16.0029 6.87913 17.1209 7.99713 18.5 7.99713C19.8791 7.99713 20.9971 6.87913 20.9971 5.5C20.9971 4.12087 19.8791 3.00287 18.5 3.00287C17.1209 3.00287 16.0029 4.12087 16.0029 5.5ZM16.0029 18.5C16.0029 19.8791 17.1209 20.9971 18.5 20.9971C19.8791 20.9971 20.9971 19.8791 20.9971 18.5C20.9971 17.1209 19.8791 16.0029 18.5 16.0029C17.1209 16.0029 16.0029 17.1209 16.0029 18.5ZM5.5 14.4943C4.12087 14.4943 3.00287 13.3763 3.00287 11.9971C3.00287 10.618 4.12087 9.5 5.5 9.5C6.87913 9.5 7.99713 10.618 7.99713 11.9971C7.99713 13.3763 6.87913 14.4943 5.5 14.4943Z" fill="#0F0F0F"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#000000"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.25 3l1.166-.624 8 5.333v1.248l-8 5.334-1.166-.624V3zm1.5 1.401v7.864l5.898-3.932L5.75 4.401z"/></svg>

After

Width:  |  Height:  |  Size: 376 B

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="-3 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<title>stats</title>
<desc>Created with Sketch Beta.</desc>
<defs>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="Icon-Set-Filled" sketch:type="MSLayerGroup" transform="translate(-314.000000, -673.000000)" fill="#000000">
<path d="M328,673 L326,673 C324.896,673 324,673.896 324,675 L324,703 C324,704.104 324.896,705 326,705 L328,705 C329.104,705 330,704.104 330,703 L330,675 C330,673.896 329.104,673 328,673 L328,673 Z M338,689 L336,689 C334.896,689 334,689.896 334,691 L334,703 C334,704.104 334.896,705 336,705 L338,705 C339.104,705 340,704.104 340,703 L340,691 C340,689.896 339.104,689 338,689 L338,689 Z M318,682 L316,682 C314.896,682 314,682.896 314,684 L314,703 C314,704.104 314.896,705 316,705 L318,705 C319.104,705 320,704.104 320,703 L320,684 C320,682.896 319.104,682 318,682 L318,682 Z" id="stats" sketch:type="MSShapeGroup">
</path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path fill="#000000" fill-rule="evenodd" d="M7.0513157,1.68377223 C7.34140089,0.813516663 8.52958649,0.773959592 8.90131717,1.56510102 L8.9486823,1.68377223 L11.999,10.838 L13.0513157,7.68377223 C13.1750557,7.31255211 13.5021221,7.05117665 13.8839548,7.00672177 L13.999999,7 L14.999999,7 C15.5522837,7 15.999999,7.44771525 15.999999,8 C15.999999,8.51283584 15.6139588,8.93550716 15.1166201,8.99327227 L14.999999,9 L14.72,9 L12.9486823,14.3162278 C12.6585971,15.1864833 11.4704115,15.2260404 11.0986808,14.434899 L11.0513157,14.3162278 L7.999999,5.161 L5.9486823,11.3162278 C5.66748671,12.1598145 4.52796777,12.2312701 4.12404882,11.4837549 L4.07152231,11.3713907 L2.961,8.596 L2.89017501,8.6833128 C2.73101502,8.85332296 2.51533249,8.96455528 2.27945404,8.99286635 L2.16,9 L0.999999,9 C0.44771425,9 -1e-06,8.55228475 -1e-06,8 C-1e-06,7.48716416 0.38603919,7.06449284 0.883377875,7.00672773 L0.999999,7 L1.495,7 L2.07801673,5.61276791 C2.40786899,4.82740446 3.48655852,4.79910618 3.87548912,5.51555754 L3.92847569,5.62860932 L4.914,8.094 L7.0513157,1.68377223 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" fill="none">
<path fill="#000000" fill-rule="evenodd" d="M5.781 4.414a7 7 0 019.62 10.039l-9.62-10.04zm-1.408 1.42a7 7 0 009.549 9.964L4.373 5.836zM10 1a9 9 0 100 18 9 9 0 000-18z"/>
</svg>

After

Width:  |  Height:  |  Size: 399 B

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 7C9.65685 7 11 5.65685 11 4C11 2.34315 9.65685 1 8 1C6.34315 1 5 2.34315 5 4C5 5.65685 6.34315 7 8 7Z" fill="#000000"/>
<path d="M14 12C14 10.3431 12.6569 9 11 9H5C3.34315 9 2 10.3431 2 12V15H14V12Z" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

@ -9,3 +9,57 @@ .scale-hover:hover { transform: scale(1.05); transition: transform 0.3s ease-in-
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes slideUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
/* Nouvelle animation : rotation au survol */
.rotate-on-hover {
transition: transform 0.5s ease-in-out;
}
.rotate-on-hover:hover {
transform: rotate(360deg);
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.pulse {
animation: pulse 2s infinite;
}
/* Nouvelle animation : Bounce */
@keyframes bounce {
0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
40% { transform: translateY(-20px); }
60% { transform: translateY(-10px); }
}
.bounce {
animation: bounce 2s infinite;
}
/* Nouvelle animation : Shake */
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
.shake {
animation: shake 0.5s ease-in-out;
}
/* Nouvelle animation : Glow on hover */
.glow-on-hover {
transition: box-shadow transform 0.1s ease-in-out;
}
.glow-on-hover:hover {
transform: scale(1.05);
box-shadow: 0 0 2px 2px rgba(51, 51, 51, 0.8);
}
@keyframes grow {
from {
width: 0%;
}
to {
width: 100%;
}
}
.grow {
animation: grow 6s linear forwards;
}

@ -14,8 +14,11 @@ const props = defineProps({
const classes = computed(() =>
props.active
? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-medium leading-5 text-textColor-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out'
: 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-textColor-500 hover:text-textColor-700 hover:border-gray-300 focus:outline-none focus:text-textColor-700 focus:border-gray-300 transition duration-150 ease-in-out',
? 'flex items-center px-1 pt-1 border-b-2 border-blue-400 text-md font-bold leading-5' +
' text-textColor-50 focus:outline-none focus:border-blue-700 transition duration-150 ease-in-out'
: 'flex items-center px-1 pt-1 border-b-2 border-transparent text-md font-medium ' +
'leading-5 text-textColor-200 hover:text-textColor-100 hover:border-blue-500 focus:outline-none ' +
'focus:text-textColor-700 focus:border-gray-300 transition duration-150 ease-in-out',
);
</script>

@ -14,8 +14,12 @@ const props = defineProps({
const classes = computed(() =>
props.active
? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 text-start text-base font-medium text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition duration-150 ease-in-out'
: 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-textColor-600 hover:text-textColor-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-textColor-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out',
? 'block w-full ps-3 pe-4 py-2 border-l-4 border-blue-400 text-start '+
'text-base font-medium text-blue-700 bg-blue-50 focus:outline-none focus:text-blue-800 '+
'focus:bg-blue-100 focus:border-blue-700 transition duration-150 ease-in-out'
: 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-textColor-600 '+
'hover:text-textColor-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-textColor-800 focus:bg-gray-50'+
' focus:border-gray-300 transition duration-150 ease-in-out',
);
</script>

@ -19,7 +19,7 @@ defineExpose({ focus: () => input.value.focus() });
<template>
<input
class="rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"
class="rounded-md border-gray-600 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"
v-model="model"
ref="input"
/>

@ -22,7 +22,7 @@ const showingNavigationDropdown = ref(false);
<div class="flex">
<!-- Logo -->
<div class="flex shrink-0 items-center">
<Link :href="route('dashboard')">
<Link :href="route('home')">
<ApplicationLogo
class="block h-9 w-auto fill-current text-textColor-800"
/>
@ -34,7 +34,7 @@ const showingNavigationDropdown = ref(false);
class="hidden space-x-8 sm:-my-px sm:ms-10 sm:flex"
>
<NavLink
:href="route('dashboard')"
:href="route('home')"
:active="route().current('dashboard')"
>
Dashboard
@ -141,7 +141,7 @@ const showingNavigationDropdown = ref(false);
>
<div class="space-y-1 pb-3 pt-2">
<ResponsiveNavLink
:href="route('dashboard')"
:href="route('home')"
:active="route().current('dashboard')"
>
Dashboard

@ -1,27 +1,76 @@
<script setup>
import { Link } from '@inertiajs/vue3';
import { Link, usePage } from '@inertiajs/vue3';
import NavLink from '@/Components/NavLink.vue';
import { onMounted, onUpdated, ref, watch } from 'vue';
import ResponsiveNavLink from '@/Components/ResponsiveNavLink.vue';
const user = usePage().props.auth.user;
const banner = usePage().props.banner;
const message = ref(usePage().props.errors.message);
const error = ref(usePage().props.errors.error);
const isFocus = ref(false);
onUpdated(() => {
if(message.value != usePage().props.errors.message) {
message.value = usePage().props.errors.message;
setTimeout(() => {
usePage().props.errors.message = null;
message.value = null;
}, 6000);
}
if(error.value != usePage().props.errors.error) {
error.value = usePage().props.errors.error;
setTimeout(() => {
usePage().props.errors.error = null;
error.value = null;
}, 6000);
}
});
message.value = usePage().props.errors.message;
error.value = usePage().props.errors.error;
usePage().props.errors.message = null;
usePage().props.errors.error = null;
</script>
<template>
<nav class="fixed top-0 right-0 left-0 h-16 w-full z-40">
<div class="w-full h-full bg-gray-900/50 flex items-center justify-between px-[10%]">
<div class="flex text-lg text-textColor-100 items-center font-medium">
<Link :href="route('home')" class="mr-4"><img src="/img/logo.svg" class="rotate-180 h-11"></Link>
<Link :href="route('home')" class="mr-5">Home</Link>
<Link :href="route('docker.create')" class="mr-5">Spawn server</Link>
<Link :href="route('docker.info')">Management</Link>
<nav class="fixed top-0 right-0 left-0 h-16 w-full z-50 bg-gray-950/45">
<div class="w-full h-full flex items-center justify-between lg:px-[10%] md:px-[5%]">
<div class="flex text-lg text-textColor-100 items-center font-medium h-full">
<Link :href="route('home')" class="md:mr-2"><img src="/img/logo.png" class="md:h-[6.8rem] h-[6.3rem]"></Link>
<NavLink :href="route('home')" :active="route().current('home')" class="mr-5 h-full md:flex items-center hidden">Home</NavLink>
<NavLink :href="route('servers.create')" :active="route().current('servers.create')" class="mr-5 h-full md:flex items-center hidden">Spawn server</NavLink>
<NavLink v-if="user"
:href="route('servers.index')" :active="route().current('servers.index') || route().current('servers.show')"
class="h-full md:flex items-center hidden">Management</NavLink>
</div>
<div v-if="!user" class="md:flex hidden text-lg text-textColor-300 items-center font-medium md:mr-5 mr-1 h-full">
<NavLink :href="route('login')" :active="route().current('login')" class="mr-5 md:flex items-center hidden h-full">Log in</NavLink>
</div>
<div class="flex text-lg text-textColor-300 items-center font-medium">
<Link :href="route('login')" class="mr-5">Login</Link>
<Link :href="route('register')">Register</Link>
<div v-else class="md:flex hidden text-lg text-textColor-300 items-center font-medium md:mr-5 mr-1 h-full">
<NavLink :href="route('logout')" :active="route().current('logout')" class="mr-5 h-full md:flex items-center hidden">Log out</NavLink>
</div>
<button @click="isFocus = !isFocus" class="md:hidden">
<img src="/icons/menu.svg" class="h-12 invert mr-1">
</button>
</div>
</nav>
<div v-show="isFocus" class="md:hidden fixed top-0 right-0 left-0 bottom-0 pt-16 w-full z-40 bg-gray-800">
<div class="flex flex-col gap-1">
<ResponsiveNavLink :href="route('home')" :active="route().current('home')" class="mr-5">Home</ResponsiveNavLink>
<ResponsiveNavLink :href="route('servers.create')" :active="route().current('servers.create')" class="mr-5">Spawn server</ResponsiveNavLink>
<ResponsiveNavLink v-if="user" :href="route('servers.index')" :active="route().current('servers.index') || route().current('servers.show')">Management</ResponsiveNavLink>
<ResponsiveNavLink v-if="!user" :href="route('login')" :active="route().current('login')" class="items-center">Log in</ResponsiveNavLink>
<ResponsiveNavLink v-else :href="route('logout')" :active="route().current('logout')" class="items-center">Log out</ResponsiveNavLink>
</div>
</div>
<main class="w-full max-h-full overflow-y-auto text-white">
<div class="w-full h-56 flex items-center justify-between overflow-hidden">
<img src="/img/cover.avif" class="w-full">
<div v-if="banner" class="w-full md:h-80 flex items-center justify-between overflow-hidden">
<img :src="banner" class="w-full pb-20">
</div>
<div v-else class="pt-20"></div>
<slot />
</main>
<!-- Footer -->
@ -31,6 +80,22 @@ import { Link } from '@inertiajs/vue3';
<p class="text-gray-400 mt-4">Code source disponible sur <a href="https://github.com/anulax1225/minecraft.anulax.ch" class="text-green-400 hover:underline">GitHub</a></p>
</section>
<footer class="bg-gray-800 text-center py-6">
<p class="text-gray-400">&copy; 2024 Minecraft Hosting - Tous droits réservés</p>
<p class="text-gray-400">&copy; 2024 Hosting - Tous droits réservés</p>
</footer>
<div v-if="message" class="fixed bottom-0 left-0 right-0 h-16 bg-green-600">
<div class="relative w-full h-full flex items-center justify-center">
<div class="absolute top-0 bottom-0 right-0 left-0">
<div class="h-full bg-green-500 grow"></div>
</div>
<p class="text-white text-2xl font-bold z-10">{{ message }}</p>
</div>
</div>
<div v-if="error" class="fixed bottom-0 left-0 right-0 h-16 bg-red-600">
<div class="relative w-full h-full flex items-center justify-center">
<div class="absolute top-0 bottom-0 right-0 left-0">
<div class="h-full bg-red-500 grow"></div>
</div>
<p class="text-white text-2xl font-bold z-10">{{ error }}</p>
</div>
</div>
</template>

@ -1,6 +1,6 @@
<script setup>
import Checkbox from '@/Components/Checkbox.vue';
import GuestLayout from '@/Layouts/GuestLayout.vue';
import Layout from '@/Layouts/Layout.vue';
import InputError from '@/Components/InputError.vue';
import InputLabel from '@/Components/InputLabel.vue';
import PrimaryButton from '@/Components/PrimaryButton.vue';
@ -30,71 +30,64 @@ const submit = () => {
</script>
<template>
<GuestLayout>
<Head title="Log in" />
<div v-if="status" class="mb-4 text-sm font-medium text-green-600">
{{ status }}
</div>
<form @submit.prevent="submit">
<div>
<InputLabel for="email" value="Email" />
<TextInput
id="email"
type="email"
class="mt-1 block w-full"
v-model="form.email"
required
autofocus
autocomplete="username"
/>
<Layout>
<div class="w-full h-screen flex items-center justify-center">
<div class="w-full max-w-md bg-gray-800 p-8 rounded-lg shadow-lg fade-in">
<!-- Boutons de bascule -->
<!-- <div class="flex justify-end mb-6">
<Link class="rounded-md text-sm text-textColor-600 underline hover:text-textColor-900
focus:outline-none">Inscription</Link>
</div> -->
<!-- Formulaire de Connexion -->
<form @submit.prevent="submit" class=" space-y-4 my-2 pt-1">
<div class="flex items-center rounded-lg">
<img src="/icons/user.svg" alt="User" class="w-6 h-6 mr-1">
<TextInput
id="email"
type="email"
class="block w-full bg-transparent focus:outline-none"
v-model="form.email"
required
autofocus
autocomplete="username"
/>
</div>
<InputError class="mt-2" :message="form.errors.email" />
</div>
<div class="mt-4">
<InputLabel for="password" value="Password" />
<TextInput
id="password"
type="password"
class="mt-1 block w-full"
v-model="form.password"
required
autocomplete="current-password"
/>
<div class="flex items-center">
<img src="/icons/lock.svg" alt="Lock" class="w-6 h-6 mr-1">
<TextInput
id="password"
type="password"
class="mt-1 block w-full bg-transparent focus:outline-none"
v-model="form.password"
required
autocomplete="current-password"
/>
</div>
<InputError class="mt-2" :message="form.errors.password" />
</div>
<div class="mt-4 block">
<label class="flex items-center">
<Checkbox name="remember" v-model:checked="form.remember" />
<span class="ms-2 text-sm text-textColor-600"
>Remember me</span
<button class="w-full bg-green-500 hover:bg-green-600 text-white py-3 rounded-lg shadow-md transition-all">
Se connecter
</button>
<div class="mt-4 flex items-center justify-between">
<label class="flex items-center">
<Checkbox name="remember" v-model:checked="form.remember" />
<span class="ms-2 text-sm text-textColor-600"
>Remember me</span
>
</label>
<Link
v-if="canResetPassword"
:href="route('password.request')"
class="rounded-md text-sm text-textColor-600 underline hover:text-textColor-900
focus:outline-none"
>
</label>
</div>
<div class="mt-4 flex items-center justify-end">
<Link
v-if="canResetPassword"
:href="route('password.request')"
class="rounded-md text-sm text-textColor-600 underline hover:text-textColor-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
Forgot your password?
</Link>
<PrimaryButton
class="ms-4"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
>
Log in
</PrimaryButton>
</div>
</form>
</GuestLayout>
Forgot your password?
</Link>
</div>
</form>
</div>
</div>
</Layout>
</template>

@ -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>

@ -1,19 +1,42 @@
<script setup>
import { Head, Link } from '@inertiajs/vue3';
import Layout from '@/Layouts/Layout.vue';
import ServerElement from './Server/Paritals/ServerElement.vue';
const props = defineProps({
services: {
type: Array,
default: []
},
servers: {
type: Array,
default: []
}
});
</script>
<template>
<Head title="Welcome" />
<Layout>
<header class="text-center py-20">
<h1 class="text-4xl font-bold animate-fade-in">Bienvenue sur Minecraft Hosting</h1>
<p class="text-gray-300 max-w-3xl mx-auto mt-4 animate-fade-in">Un service gratuit permettant d'héberger facilement vos serveurs Minecraft dans des conteneurs Docker sécurisés et performants.</p>
<header class="text-center my-10">
<h1 class="text-4xl font-bold fade-in flex justify-center items-center gap-2">
<img src="/icons/server-icon.svg" alt="Serveur" class="md:block hidden w-12 h-12 invert"> Bienvenue sur Hosting
</h1>
<p class="text-gray-300 max-w-3xl mx-auto mt-4 fade-in">Un service gratuit permettant d'héberger facilement vos serveurs dans des conteneurs Docker sécurisés et performants.</p>
</header>
<p class="text-center text-2xl font-bold mb-4 fade-in">Nos Services</p>
<section class="flex md:flex-row flex-col items-center md:justify-center justify-between mx-auto mb-10 slide-up">
<div v-for="service in props.services" class="relative flex items-center justify-center h-72 max-w-52 overflow-x-hidden bg-black w-full rounded-lg my-2 mx-5">
<img :src="service.image" class="object-cover h-full">
<!-- <p class="absolute bottom-0 right-0 left-0 p-1 text-center bg-gray-950/60 font-extrabold text-gray-300 text-2xl">{{ service.name }}</p> -->
</div>
</section>
<section class="max-w-4xl mx-auto bg-gray-800 p-6 rounded-lg shadow-lg animate-slide-up">
<h2 class="text-2xl font-bold mb-4">Pourquoi choisir notre hébergement ?</h2>
<section class="max-w-4xl mx-auto bg-gray-800 p-6 rounded-lg shadow-lg slide-up">
<h2 class="text-2xl font-bold mb-4 flex items-center gap-2">
<img src="/icons/security-icon.svg" alt="Sécurité" class="w-6 h-6 invert">Comment fonctionne notre hébergement ?
</h2>
<ul class="list-disc list-inside text-gray-300">
<li>Infrastructure basée sur Docker pour une isolation et une sécurité optimales.</li>
<li>Stockage limité à 10 Go par serveur pour éviter toute surcharge.</li>
@ -23,29 +46,40 @@ import Layout from '@/Layouts/Layout.vue';
</section>
<section class="mt-10 text-center">
<h2 class="text-2xl font-bold mb-4 animate-fade-in">Comment ça fonctionne ?</h2>
<div class="max-w-4xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-6 animate-slide-up">
<div class="bg-gray-800 p-6 rounded-lg shadow-lg">
<h2 class="text-2xl font-bold mb-4 fade-in flex justify-center items-center gap-2">
<img src="/icons/process-icon.svg" alt="Processus" class="w-7 h-7 invert">Démarche à sur suivre ?
</h2>
<div class="max-w-4xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-6 slide-up">
<div class="bg-gray-800 p-6 rounded-lg shadow-lg flex flex-col items-center">
<img src="/icons/start-icon.svg" alt="Créer" class="w-14 h-14 mb-2 invert">
<h3 class="text-xl font-semibold">1. Créez votre serveur</h3>
<p class="text-gray-300">Remplissez un simple formulaire pour générer votre serveur.</p>
</div>
<div class="bg-gray-800 p-6 rounded-lg shadow-lg">
<div class="bg-gray-800 p-6 rounded-lg shadow-lg flex flex-col items-center">
<img src="/icons/play-icon.svg" alt="Lancer" class="w-14 h-14 mb-2 invert">
<h3 class="text-xl font-semibold">2. Lancez et jouez</h3>
<p class="text-gray-300">Votre serveur est déployé instantanément et prêt à être rejoint.</p>
</div>
<div class="bg-gray-800 p-6 rounded-lg shadow-lg">
<div class="bg-gray-800 p-6 rounded-lg shadow-lg flex flex-col items-center">
<img src="/icons/auto-icon.svg" alt="Auto-gestion" class="w-14 h-14 mb-2 invert">
<h3 class="text-xl font-semibold">3. Auto-gestion</h3>
<p class="text-gray-300">Le serveur s'éteint automatiquement après inactivité pour économiser les ressources.</p>
</div>
</div>
</section>
<section class="max-w-4xl mx-auto text-center bg-cover bg-center fade-in my-10" style="background-image: url('https://source.unsplash.com/1600x900/?minecraft,game');">
<div class="w-full bg-gray-800 p-10 inline-block rounded-lg slide-up">
<h2 class="text-4xl font-bold">Hébergez votre serveur Minecraft</h2>
<p class="text-gray-300 mt-4">Des performances optimales pour des parties sans lag. Commencez dès maintenant !</p>
<a href="#offres" class="mt-6 inline-block bg-green-500 hover:bg-green-600 text-white font-bold py-3 px-6 rounded-lg shadow-md scale-hover">Créer un serveur</a>
</div>
<section class="mt-10 text-center mb-10">
<h2 class="text-2xl font-bold mb-4 fade-in flex justify-center items-center gap-2">
<img src="/icons/rocket-icon.svg" alt="Démarrer" class="w-7 h-7 invert"> Commencez dès maintenant !
</h2>
<p class="text-gray-300 mt-4 fade-in">Des performances optimales pour des parties sans lag. Commencez dès maintenant !</p>
<Link :href="route('servers.create')" class="mt-6 w-fit mx-auto items-center fade-in flex bg-green-500 hover:bg-green-600 text-white font-bold py-3 px-6 rounded-lg shadow-md scale-hover">
<img src="/icons/arrow-icon.svg" alt="Créer" class="w-5 h-5 rotate-180 invert mr-2 object-cover"> Créer un serveur
</Link>
</section>
<section class="max-w-4xl mx-auto grid grid-cols-1 md:grid-cols-2 gap-6 my-10">
<ServerElement v-for="server in props.servers" :server="server" :editable="false"/>
</section>
</Layout>
</template>

@ -6,14 +6,31 @@ import axios from 'axios';
import Layout from '@/Layouts/Layout.vue';
import InputError from '@/Components/InputError.vue';
import InputLabel from '@/Components/InputLabel.vue';
import { computed } from 'vue';
const props = defineProps({
services: {
type: Object,
default: []
}
});
const form = useForm({
name: "",
port: "25000",
service: props.services[0].uuid,
launch: false,
});
const formatPort = computed(() => {
let result = "port";
let nb = props.services.find(s => form.service == s.uuid).ports.split("|").length;
for(let i = 1; i < nb; i++) result = result + "|port";
result += ")"
return result;
});
const submit = () => {
form.post("/spawn");
form.post(route("servers.store"));
}
console.log();
@ -24,18 +41,25 @@ console.log();
<Layout>
<!-- Création de Serveur -->
<section class="py-20 px-6 text-center">
<h2 class="text-3xl font-bold mb-10 fade-in">Créer Votre Serveur Minecraft Gratuitement</h2>
<h2 class="text-3xl font-bold mb-10 fade-in">Créer Votre Serveur Gratuitement</h2>
<p class="text-gray-300 max-w-3xl mx-auto fade-in">Nos serveurs sont isolés via Docker et offrent un stockage limité à 10Go. Pour économiser les ressources, un serveur inactif pendant une période prolongée sera automatiquement arrêté.</p>
<form @submit.prevent="submit" class="max-w-lg mx-auto bg-gray-800 p-8 rounded-lg shadow-lg slide-up mt-6">
<div class="mb-6">
<div class="mb-3">
<InputLabel class="block text-left text-gray-300 mb-2" for="name">Name</InputLabel>
<TextInput v-model="form.name" type="text" class="w-full p-3 rounded bg-gray-700 text-white" placeholder="Server name"></TextInput>
<InputError :message="form.errors.name"></InputError>
<InputError :message="form.errors.name" class="text-left mt-1"></InputError>
</div>
<div class="mb-4">
<InputLabel class="block text-left text-gray-300 mb-2" for="service">Service</InputLabel>
<select @change="e => form.service = e.target.value"
class="w-full p-3 rounded bg-gray-700 text-white border-gray-600 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option v-for="service in props.services" :value="service.uuid">{{ service.name }}</option>
</select>
</div>
<div class="mb-6">
<InputLabel class="block text-left text-gray-300 mb-2" for="port">Port</InputLabel>
<TextInput v-model="form.port" type="text" class="w-full p-3 rounded bg-gray-700 text-white" placeholder="Port (ex: 25000)"></TextInput>
<InputError :message="form.errors.port"></InputError>
<InputError :message="form.errors.service" class="text-left mt-1"></InputError>
<div class="flex items-center mb-6">
<p class="text-textColor-700 mr-3">Lancer maintenant</p>
<input type="checkbox" class="rounded bg-gray-500" @click="() => form.launch = !form.launch">
</div>
<button type="submit" class="w-full bg-green-500 hover:bg-green-600 text-white font-bold py-3 px-6 rounded-lg shadow-md scale-hover">Créer le Serveur</button>
</form>

@ -0,0 +1,29 @@
<script setup>
import Layout from '@/Layouts/Layout.vue';
import { Head, useForm } 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 et plus encore (Minecraft).</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,73 @@
<script setup>
import { Link, useForm } from '@inertiajs/vue3';
const props = defineProps({
server: {
type: Object,
required: true
},
editable: {
type: Boolean,
default: true
}
});
if(props.editable) {
}
const form = useForm();
const start = (uuid) => {
form.post(route("servers.start", uuid), {
onSuccess: () => {
vm.$forceUpdate()
}
});
}
const stop = (uuid) => {
form.post(route("servers.stop", uuid));
}
</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 v-if="props.editable" ><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 && props.server.status.id < 3 ? "hosting.anulax.ch:" + props.server.ports[0] : "Indisponible" }}
</p>
</p>
</div>
<!-- Actions -->
<div v-if="props.editable" class="mt-4 flex gap-2 w-full justify-end">
<div v-if="props.server.status.id >= 3" @click="start(props.server.uuid)" class="bg-green-500 hover:bg-green-600 text-white f
glow-on-hover 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">
</div>
<div v-else @click="stop(props.server.uuid)" class="bg-red-500 hover:bg-red-600 text-white
glow-on-hover 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">
</div>
<Link :href="route('servers.show', props.server.uuid)" class="bg-blue-500 hover:bg-blue-600 text-white
glow-on-hover 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,195 @@
<script setup>
import InputError from '@/Components/InputError.vue';
import TextInput from '@/Components/TextInput.vue';
import Layout from '@/Layouts/Layout.vue';
import { Head, useForm } from '@inertiajs/vue3';
import { ref } from 'vue';
const props = defineProps({
server: {
type: Object,
required: true
}
});
const isFocus = ref(false);
const form = useForm();
const editForm = useForm({ name: props.server.name });
const start = (uuid) => {
form.post(route("servers.start", uuid));
}
const stop = (uuid) => {
form.post(route("servers.stop", uuid));
}
const del = (uuid) => {
if (prompt("Entrer le nom du server en confirmation.") == props.server.name) form.delete(route("servers.destroy", uuid));
}
const pub = (uuid) => {
if (props.server.public) form.post(route("servers.public", uuid));
else if (prompt("Entrer le nom du server en confirmation.") == props.server.name) form.post(route("servers.public", uuid));
}
const edit = () => {
console.log(editForm.name);
editForm.post(route("servers.update", props.server.uuid));
}
const copie = () => {
navigator.clipboard.writeText('hosting.anulax.ch:' + props.server.ports[0]).then(() => alert("lien de connection copié!"))
}
</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 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-2 lg:grid-cols-3 gap-3 xl:gap-6 mb-6 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 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">{{ props.server.name }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Type de jeu</span>
<span>{{ props.server.service.name }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Public</span>
<span>{{ props.server.public ? "Oui" : "Non" }}</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 lg:hidden">Lien de connection</span>
<span class="font-medium xl:hidden lg:block hidden">Lien du server</span>
</div>
<div class="flex items-center gap-2">
<p class="text-green-400 hover:underline">
{{ props.server.ports.length && props.server.status.id < 3 ? "hosting.anulax.ch:" +
props.server.ports[0] : "Indisponible" }} </p>
<button v-show="props.server.ports.length && props.server.status.id < 3"
@click="copie"
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 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">{{ props.server.status.title }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Dernier lancement</span>
<span class="text-green-400 font-bold">{{ props.server.start ? props.server.start : "Aucun"
}}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Dernier arrêt </span>
<span class="text-red-400 font-bold">{{ props.server.end ? props.server.end : "Aucun" }}</span>
</div>
</div>
<div class="flex gap-3">
<div @click="start(props.server.uuid)" v-if="props.server.status.id >= 3"
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">
</div>
<div @click="stop(props.server.uuid)" v-else
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">
</div>
</div>
</div>
<!-- Carte : Hardware -->
<div class="bg-gray-800 rounded-lg shadow-lg p-6 lg:col-span-1 md:col-span-2 col-span-1">
<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 invert">
Hardware(fake)
</h2>
<div class="space-y-3 text-sm">
<div class="flex justify-between">
<span class="font-medium">CPUs</span>
<span>1</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Memory</span>
<span>4GB</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Stockage</span>
<span>10GB</span>
</div>
</div>
</div>
</main>
<!-- Section Actions en haut -->
<section class="max-w-6xl mx-auto mb-10 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 items-center">
<form v-show="isFocus" @submit.prevent="edit">
<TextInput v-model="editForm.name" type="text"
class="w-full px-3 py-1 rounded bg-gray-700 text-white" />
<button type="submit"></button>
<InputError :message="form.errors.name" class="text-left mt-1"></InputError>
</form>
<button @click="isFocus = !isFocus"
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">
</button>
<button v-if="props.server.public" @click="pub(props.server.uuid)"
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/lock.svg" alt="Rendre Privé" class="w-5 h-5 invert">
</button>
<button v-else @click="pub(props.server.uuid)"
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>
<button @click="del(props.server.uuid)"
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">
</button>
</div>
</div>
</section>
</Layout>
</template>

@ -15,7 +15,7 @@
@vite(['resources/js/app.js', "resources/js/Pages/{$page['component']}.vue"])
@inertiaHead
</head>
<body class="font-roboto antialiased w-screen h-screen overflow-x-hidden bg-gray-600 dark">
<body class="font-roboto antialiased w-screen h-screen overflow-x-hidden bg-gray-900 dark">
@inertia
</body>
</html>

@ -12,10 +12,10 @@
use Illuminate\Support\Facades\Route;
Route::middleware('guest')->group(function () {
Route::get('register', [RegisteredUserController::class, 'create'])
->name('register');
// Route::get('register', [RegisteredUserController::class, 'create'])
// ->name('register');
Route::post('register', [RegisteredUserController::class, 'store']);
// Route::post('register', [RegisteredUserController::class, 'store']);
Route::get('login', [AuthenticatedSessionController::class, 'create'])
->name('login');
@ -54,6 +54,6 @@
Route::put('password', [PasswordController::class, 'update'])->name('password.update');
Route::post('logout', [AuthenticatedSessionController::class, 'destroy'])
Route::get('logout', [AuthenticatedSessionController::class, 'destroy'])
->name('logout');
});

@ -1,8 +1,38 @@
<?php
use App\Docker\Container;
use App\Models\User;
use Carbon\Carbon;
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('create:user {name} {email} {password} {admin}', function ($name, $email, $password, $admin) {
User::create([
"name" => $name,
"email" => $email,
"password" => bcrypt($password),
"admin" => $admin,
"email_verified_at" => Carbon::now()
]);
})->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');

@ -2,6 +2,10 @@
use App\Docker\Docker;
use App\Docker\Container;
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\ServerController;
use App\Http\Middleware\ResourceRules;
use App\Models\Server;
use App\Models\Service;
use Illuminate\Foundation\Application;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
@ -15,7 +19,11 @@
Route::get('/', function () {
return Inertia::render('Home');
return Inertia::render('Home', [
"banner" => "/img/banner.avif",
"services" => Service::all(),
"servers" => Server::where("public", true)->get()->jsonSerialize()
]);
})->name("home");
Route::get("/info", function() {
@ -23,36 +31,21 @@
return Inertia::render('Info', [ "config" => $config ]);
})->name("docker.info");
Route::get("/spawn", function (Request $request) {
return Inertia::render('Spawn');
})->name("docker.create");
Route::post("/spawn", function (Request $request) {
$request->validate([
"name" => "required|string",
"port" => "required|string"
]);
$container = Container::create($request->name, [
"Hostname" => "mincraft-". $request->name,
"Domainname" => $request->name,
"User" => "root",
"Image" => "minecraft:latest",
"OpenStdin" => true,
"Tty" => true,
"ExposedPorts" => [ "25565/tcp" => (object)[] ],
"HostConfig" => [
"PortBindings" => [ "25565/tcp" => [[ "HostPort" => $request->port ]] ],
]
]);
$container->start();
return redirect("/");
});
Route::get('/dashboard', function () {
return Inertia::render('Dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
Route::middleware('auth')->group(function () {
Route::middleware(['auth'])->group(function () {
Route::get("/servers/create", [ServerController::class, 'create'])->name("servers.create");
Route::post("/servers", [ServerController::class, 'store'])->name("servers.store");
Route::get("/servers", [ServerController::class, 'index'])->name("servers.index");
Route::middleware(ResourceRules::class)->group(function() {
Route::get("/servers/{id}", [ServerController::class, 'show'])->name("servers.show");
Route::post("/servers/{id}/update", [ServerController::class, 'update'])->name("servers.update");
Route::post("/servers/{id}/public", [ServerController::class, 'makePublic'])->name("servers.public");
Route::delete("/servers/{id}", [ServerController::class, 'destroy'])->name("servers.destroy");
Route::post("/servers/{id}/start", [ServerController::class, 'start'])->name("servers.start");
Route::post("/servers/{id}/stop", [ServerController::class, 'stop'])->name("servers.stop");
});
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');

@ -0,0 +1,2 @@
*
!.gitignore

@ -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:{}}}

11
svg

@ -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"

@ -27,7 +27,7 @@ public function test_users_can_authenticate_using_the_login_screen(): void
]);
$this->assertAuthenticated();
$response->assertRedirect(route('dashboard', absolute: false));
$response->assertRedirect(route('home', absolute: false));
}
public function test_users_can_not_authenticate_with_invalid_password(): void

@ -38,7 +38,7 @@ public function test_email_can_be_verified(): void
Event::assertDispatched(Verified::class);
$this->assertTrue($user->fresh()->hasVerifiedEmail());
$response->assertRedirect(route('dashboard', absolute: false).'?verified=1');
$response->assertRedirect(route('home', absolute: false).'?verified=1');
}
public function test_email_is_not_verified_with_invalid_hash(): void

@ -26,6 +26,6 @@ public function test_new_users_can_register(): void
]);
$this->assertAuthenticated();
$response->assertRedirect(route('dashboard', absolute: false));
$response->assertRedirect(route('home', absolute: false));
}
}

@ -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
}
]
}
}
}
Loading…
Cancel
Save