Skip to content
Snippets Groups Projects
Unverified Commit c08649ad authored by Maxime FRIESS's avatar Maxime FRIESS :blue_heart:
Browse files

Merge branch 'release/1.2.0'

parents 3535ece7 225f9dc8
Branches
Tags 1.2.0
No related merge requests found
Pipeline #105976 passed with stages
in 55 seconds
......@@ -176,4 +176,37 @@ public function heartbeat(ServerHeartbeatRequest $request)
$server->heartbeat($request->input("ip"), $request->input("port"), $request->input("pubkey"));
return response()->noContent();
}
/**
* @OA\Post(
* tags={"Servers:Authentication"},
* path="/auth/server/stop",
* summary="Singla to the API that the server has stopped",
* @OA\Response(
* response="401",
* ref="#/components/responses/401"
* ),
* @OA\Response(
* response="403",
* description="Server is offline",
* ),
* @OA\Response(
* response="204",
* description="OK",
* ),
* )
*/
public function stop(Request $request)
{
$server = Auth::guard('server')->user();
if (!$server->online) {
return response()->json([
'message' => 'Server is offline',
], 403);
}
$server->stop();
return response()->noContent();
}
}
......@@ -35,22 +35,7 @@ public function handle()
// We grab the server that are marked online but didn't do a heartbeat in the last two minutes.
// This happens when a server crashes or goes offline.
Server::where("online", true)->where('last_heartbeat_at', '<', Carbon::now()->subSeconds(120)->toDateTimeString())->lazyById()->each(function ($server) {
// We fetch all the users connected to the server and mark them as disconnected.
// NOTE: The users will have to wait at least one minute and at most three minutes
// since the server crashes before connecting to another server.
$server->users->each(function ($user) {
$user->disconnect();
});
// We fetch all the game that were running, detach their users (thus deleting the pivot data)
// and delete the game. This makes it as the game never happened.
$server->games->whereIn("state", ["created", "playing"])->each(function($game) {
$game->users()->detach();
$game->delete();
});
// We mark the server as offline
$server->offline();
$server->stop();
});
}
}
......@@ -113,4 +113,24 @@ public function offline()
$this->pubkey = null;
$this->save();
}
public function stop()
{
// We fetch all the users connected to the server and mark them as disconnected.
// NOTE: The users will have to wait at least one minute and at most three minutes
// since the server crashes before connecting to another server.
$this->users->each(function ($user) {
$user->disconnect();
});
// We fetch all the game that were running, detach their users (thus deleting the pivot data)
// and delete the game. This makes it as the game never happened.
$this->games->whereIn("state", ["created", "playing"])->each(function($game) {
$game->users()->detach();
$game->delete();
});
// We mark the server as offline
$this->offline();
}
}
\ No newline at end of file
......@@ -4,7 +4,7 @@
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
"license": "MIT",
"version": "1.1.0",
"version": "1.2.0",
"require": {
"php": "^8.0.2",
"darkaonline/l5-swagger": "^8.4",
......
......@@ -18,7 +18,7 @@ services:
database:
condition: service_healthy
build: .
command: sh -c "php artisan migrate --force && php artisan serve --host 0.0.0.0"
command: sh -c "php artisan migrate --force && php artisan l5-swagger:generate && php artisan serve --host 0.0.0.0"
image: registry.app.unistra.fr/bombernyan/api
volumes:
- storage:/api/storage
......
......@@ -38,6 +38,7 @@
Route::post('refresh', [Server\AuthController::class, 'refresh']);
Route::get('profile', [Server\AuthController::class, 'profile']);
Route::post('heartbeat', [Server\AuthController::class, 'heartbeat']);
Route::post('stop', [Server\AuthController::class, 'stop']);
});
});
......
<?php
namespace Tests\Feature;
use App\Jobs\CheckServers;
use App\Models\Game;
use App\Models\Server;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Tests\TestCase;
class StopTest extends TestCase
{
use RefreshDatabase;
public function test_stop()
{
$user = User::create([
'username' => 'test',
'email' => 'test@localhost',
'password' => Hash::make('test'),
]);
$user2 = User::create([
'username' => 'test2',
'email' => 'test2@localhost',
'password' => Hash::make('test'),
]);
$server = Server::create([
'login' => 'test',
'password' => Hash::make('test'),
'owner_id' => $user->id,
'public' => true
]);
$user->server_id = $server->id;
$user->server_state = 'connected';
$user->server_token = null;
$user->save();
$user2->server_id = $server->id;
$user2->server_state = 'connected';
$user2->server_token = null;
$user2->save();
$server->heartbeat('127.0.0.1', 12345, 'pubkey');
$server->last_heartbeat_at = Carbon::now()->subMinutes(5);
$server->save();
$game = Game::create([
"server_id" => $server->id
]);
$game->users()->attach($user->id);
$game->users()->attach($user2->id);
$server = $server->refresh();
$this->assertEquals(true, $server->online);
$this->assertEquals("127.0.0.1", $server->ip);
$this->assertEquals(12345, $server->port);
$this->assertEquals("pubkey", $server->pubkey);
$server_token = Auth::guard('server')->login($server);
$response = $this->withHeaders([
"Authorization" => "Bearer " . $server_token
])->post('/api/auth/server/stop');
$response->assertStatus(204);
$server = $server->refresh();
$this->assertEquals(null, $server->online);
$this->assertEquals(null, $server->ip);
$this->assertEquals(null, $server->port);
$this->assertEquals(null, $server->pubkey);
$user = $user->refresh();
$this->assertEquals(null, $user->server_id);
$this->assertEquals("offline", $user->server_state);
$this->assertEquals(null, $user->server_token);
$user2 = $user2->refresh();
$this->assertEquals(null, $user2->server_id);
$this->assertEquals("offline", $user2->server_state);
$this->assertEquals(null, $user2->server_token);
$this->assertDatabaseCount("games", 0);
$this->assertDatabaseCount("users_games", 0);
}
public function test_stop_game_finished()
{
$user = User::create([
'username' => 'test',
'email' => 'test@localhost',
'password' => Hash::make('test'),
]);
$user2 = User::create([
'username' => 'test2',
'email' => 'test2@localhost',
'password' => Hash::make('test'),
]);
$server = Server::create([
'login' => 'test',
'password' => Hash::make('test'),
'owner_id' => $user->id,
'public' => true
]);
$user->server_id = $server->id;
$user->server_state = 'connected';
$user->server_token = null;
$user->save();
$user2->server_id = $server->id;
$user2->server_state = 'connected';
$user2->server_token = null;
$user2->save();
$server->heartbeat('127.0.0.1', 12345, 'pubkey');
$server->last_heartbeat_at = Carbon::now()->subMinutes(5);
$server->save();
$game = Game::create([
"server_id" => $server->id
]);
$game->users()->attach($user->id);
$game->users()->attach($user2->id);
$game->state = 'finished';
$game->save();
$server = $server->refresh();
$this->assertEquals(true, $server->online);
$this->assertEquals("127.0.0.1", $server->ip);
$this->assertEquals(12345, $server->port);
$this->assertEquals("pubkey", $server->pubkey);
$server_token = Auth::guard('server')->login($server);
$response = $this->withHeaders([
"Authorization" => "Bearer " . $server_token
])->post('/api/auth/server/stop');
$response->assertStatus(204);
$server = $server->refresh();
$this->assertEquals(null, $server->online);
$this->assertEquals(null, $server->ip);
$this->assertEquals(null, $server->port);
$this->assertEquals(null, $server->pubkey);
$user = $user->refresh();
$this->assertEquals(null, $user->server_id);
$this->assertEquals("offline", $user->server_state);
$this->assertEquals(null, $user->server_token);
$user2 = $user2->refresh();
$this->assertEquals(null, $user2->server_id);
$this->assertEquals("offline", $user2->server_state);
$this->assertEquals(null, $user2->server_token);
$this->assertDatabaseCount("games", 1);
$this->assertDatabaseCount("users_games", 2);
}
public function test_stop_offline()
{
$user = User::create([
'username' => 'test',
'email' => 'test@localhost',
'password' => Hash::make('test'),
]);
$server = Server::create([
'login' => 'test',
'password' => Hash::make('test'),
'owner_id' => $user->id,
'public' => true
]);
$server_token = Auth::guard('server')->login($server);
$response = $this->withHeaders([
"Authorization" => "Bearer " . $server_token
])->post('/api/auth/server/stop');
$response->assertStatus(403);
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment