From 48846ea523693372037f516d44b1512b34d34aed Mon Sep 17 00:00:00 2001 From: "Dest.Com" <37459465+DestroyCom@users.noreply.github.com> Date: Thu, 14 Sep 2023 18:23:23 +0200 Subject: [PATCH 01/27] docs: fix lucalg322 github link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bca4911..e3d7df1 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ sh dev.sh - [@DestroyCom](https://github.com/DestroyCom) - [@ADR1811](https://github.com/ADR1811) -- [@lucag322](https://github.com/ADR1811) +- [@lucag322](https://github.com/lucag322) - [@ThomAzgo](https://github.com/ThomAzgo) - [@Hiteazel](https://github.com/Hiteazel) From f7fb508b80e625953eb1fe5b41f384e3598a102e Mon Sep 17 00:00:00 2001 From: Adrien Albuquerque Date: Thu, 14 Sep 2023 23:19:42 +0200 Subject: [PATCH 02/27] =?UTF-8?q?feat(Project):=20Publish=20Unpublish=20?= =?UTF-8?q?=F0=9F=A4=8C=F0=9F=A4=8C=F0=9F=A4=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Http/Controllers/ProjectController.php | 6 +- .../app/Http/Controllers/StepController.php | 134 +++++++++++++++ sera-back/config/roles.php | 4 +- .../database/factories/ProjectFactory.php | 2 +- ...023_07_04_134808_create_projects_table.php | 3 +- sera-back/routes/api.php | 2 + sera-back/storage/api-docs/api-docs.json | 162 +++++++++++++++++- 7 files changed, 304 insertions(+), 9 deletions(-) diff --git a/sera-back/app/Http/Controllers/ProjectController.php b/sera-back/app/Http/Controllers/ProjectController.php index f1a562e..ba601c6 100644 --- a/sera-back/app/Http/Controllers/ProjectController.php +++ b/sera-back/app/Http/Controllers/ProjectController.php @@ -69,7 +69,7 @@ public function index(Request $request) $request->validate([ 'maxPerPage' => 'integer', 'sort' => 'string|in:asc,desc', - 'status' => 'string|in:ongoing,completed,cancelled', + 'status' => 'string|in:ongoing,published,cancelled', ]); $maxPerPage = $request->input('maxPerPage', 10); @@ -366,7 +366,7 @@ public function show($id) * @OA\Property( * property="status", * type="string", - * enum={"ongoing", "completed", "cancelled"}, + * enum={"ongoing", "published", "cancelled"}, * ), * @OA\Property( * property="change_color", @@ -435,7 +435,7 @@ public function update(Request $request, $id): JsonResponse 'description' => 'string', 'start_date' => 'date', 'end_date' => 'date', - 'status' => 'string|in:ongoing,completed,cancelled', + 'status' => 'string|in:ongoing,published,cancelled', 'change_color' => 'boolean', ]); diff --git a/sera-back/app/Http/Controllers/StepController.php b/sera-back/app/Http/Controllers/StepController.php index cf4704f..93803f8 100644 --- a/sera-back/app/Http/Controllers/StepController.php +++ b/sera-back/app/Http/Controllers/StepController.php @@ -654,4 +654,138 @@ public function validateTranscription(Request $request, $project_id){ return response()->json($transcriptions, 200); } + + /** + * @OA\Post( + * path="/api/projects/{project_id}/publish", + * tags={"Projects"}, + * summary="Publish project", + * description="Publish project", + * operationId="PublishProject", + * @OA\Parameter( + * description="Project id", + * in="path", + * name="project_id", + * required=true, + * @OA\Schema( + * type="integer", + * format="int64" + * ) + * ), + * @OA\Response( + * response=200, + * description="Project published", + * @OA\JsonContent( + * @OA\Property(property="id", type="integer", example="1"), + * @OA\Property(property="title", type="string", example="Project title"), + * @OA\Property(property="description", type="string", example="Project description"), + * @OA\Property(property="status", type="string", example="published"), + * @OA\Property(property="created_at", type="string", example="2021-05-05T14:48:00.000000Z"), + * @OA\Property(property="updated_at", type="string", example="2021-05-05T14:48:00.000000Z"), + * @OA\Property(property="project_request_id", type="integer", example="1"), + * ), + * ), + * @OA\Response( + * response=400, + * description="Bad request", + * @OA\JsonContent( + * @OA\Property(property="error", type="string", example="Project has no subtitles."), + * ), + * ), + * ) + */ + public function publish($project_id){ + $project = Project::find($project_id); + + if ($project === null) { + return response()->json(['error' => 'Project not found.'], 404); + } + + $steps = json_decode($project->steps); + + // il faut que toutes les steps soient done et que Subtitling soit ongoing + foreach ($steps as $step => $value) { + if($value->status !== 'done' && $step !== 'Subtitling' && $step !== 'Editorial'){ + return response()->json(['error' => 'Step ' . $step . ' is not done.'], 400); + } + } + + $subtitles = $project->subtitles()->get(); + + // is empty + if($subtitles->isEmpty()){ + return response()->json(['error' => 'Project has no subtitles.'], 400); + } + + $editorials = $project->edito()->get(); + + if($editorials->isEmpty()){ + return response()->json(['error' => 'Project has no editorials.'], 400); + } + // on passe le status du projet à published + $project->status = 'published'; + + $project->save(); + + return response()->json('Project published' , 200); + } + + /** + * @OA\Post( + * path="/api/projects/{project_id}/unpublish", + * tags={"Projects"}, + * summary="Unpublish project", + * description="Unpublish project", + * operationId="UnpublishProject", + * @OA\Parameter( + * description="Project id", + * in="path", + * name="project_id", + * required=true, + * @OA\Schema( + * type="integer", + * format="int64" + * ) + * ), + * @OA\Response( + * response=200, + * description="Project unpublished", + * @OA\JsonContent( + * @OA\Property(property="id", type="integer", example="1"), + * @OA\Property(property="title", type="string", example="Project title"), + * @OA\Property(property="description", type="string", example="Project description"), + * @OA\Property(property="status", type="string", example="ongoing"), + * @OA\Property(property="created_at", type="string", example="2021-05-05T14:48:00.000000Z"), + * @OA\Property(property="updated_at", type="string", example="2021-05-05T14:48:00.000000Z"), + * @OA\Property(property="project_request_id", type="integer", example="1"), + * ), + * ), + * @OA\Response( + * response=400, + * description="Bad request", + * @OA\JsonContent( + * @OA\Property(property="error", type="string", example="Project is not published."), + * ), + * ), + * ) + */ + public function unpublish($project_id){ + $project = Project::find($project_id); + + if ($project === null) { + return response()->json(['error' => 'Project not found.'], 404); + } + + // il faut que le status du projet soit published + if($project->status !== 'published'){ + return response()->json(['error' => 'Project is not published.'], 400); + } + + $project->status = 'ongoing'; + + $project->save(); + + return response()->json('Project unpublished' , 200); + } + } diff --git a/sera-back/config/roles.php b/sera-back/config/roles.php index 57eec44..ef241dd 100644 --- a/sera-back/config/roles.php +++ b/sera-back/config/roles.php @@ -6,7 +6,7 @@ "cursus_director" => [ "projects-requests" => ["show", "index", "store", "update", "destroy"], "users" => ["iso","show", "index", "store", "update", "destroy", "roles", "image", "password","reservations","reservations"], - "projects" => ["validateTranscription","getCaptionUrl","show", "index", "store", "update", "destroy", "init","stepsGet","stepsUpdateDate","planificationToCaptation","addLink","captationToPostproduction","validatePostProd"], + "projects" => ["unpublish","publish","validateTranscription","getCaptionUrl","show", "index", "store", "update", "destroy", "init","stepsGet","stepsUpdateDate","planificationToCaptation","addLink","captationToPostproduction","validatePostProd"], "teams" => ["add", "index", "show", "remove"], "rooms" => ["show", "index", "store", "update", "destroy", "reserve","unreserve","available","showByProject"], "video-reviews" => ["getReviewsByProjectId","store","destroy","addAComment","getTemporaryUploadUrl","getVideoValidated"], @@ -20,7 +20,7 @@ "project_manager" => [ "projects-requests" => ["show", "index", "update"], "users" => ["iso","show", "index", "store", "update", "destroy", "roles", "image", "password","reservations"], - "projects" => ["validateTranscription","getCaptionUrl","show", "index", "store", "update", "destroy", "init","stepsGet","stepsUpdateDate","planificationToCaptation","addLink","captationToPostproduction","validatePostProd"], + "projects" => ["unpublish","publish","validateTranscription","getCaptionUrl","show", "index", "store", "update", "destroy", "init","stepsGet","stepsUpdateDate","planificationToCaptation","addLink","captationToPostproduction","validatePostProd"], "teams" => ["add", "index", "show", "remove"], "rooms" => ["show", "index", "reserve","unreserve","available","showByProject"], "video-reviews" => ["getReviewsByProjectId","store","destroy","addAComment","getTemporaryUploadUrl","getVideoValidated"], diff --git a/sera-back/database/factories/ProjectFactory.php b/sera-back/database/factories/ProjectFactory.php index b7874e6..3e29f40 100644 --- a/sera-back/database/factories/ProjectFactory.php +++ b/sera-back/database/factories/ProjectFactory.php @@ -19,7 +19,7 @@ class ProjectFactory extends Factory */ public function definition(): array { - $statusArray = ["ongoing", "completed", "cancelled"]; + $statusArray = ["ongoing", "published", "cancelled"]; $defaultSteps = config('steps'); $projectRequest = ProjectRequest::factory()->create(); diff --git a/sera-back/database/migrations/2023_07_04_134808_create_projects_table.php b/sera-back/database/migrations/2023_07_04_134808_create_projects_table.php index 1f6ea35..5aabeac 100644 --- a/sera-back/database/migrations/2023_07_04_134808_create_projects_table.php +++ b/sera-back/database/migrations/2023_07_04_134808_create_projects_table.php @@ -17,12 +17,11 @@ public function up(): void Schema::create('projects', function (Blueprint $table) { $defaultSteps = config('steps'); - $table->id(); $table->foreignId('project_request_id')->constrained('project_requests')->cascadeOnDelete()->cascadeOnUpdate(); $table->string('title'); $table->longText('description'); - $table->enum('status', ['ongoing', 'completed', 'cancelled'])->default('ongoing'); + $table->enum('status', ['ongoing', 'published', 'cancelled'])->default('ongoing'); $table->string('start_date')->nullable(); $table->string('end_date')->nullable(); $table->json('colors')->nullable(); diff --git a/sera-back/routes/api.php b/sera-back/routes/api.php index b1573ff..94dd3bf 100644 --- a/sera-back/routes/api.php +++ b/sera-back/routes/api.php @@ -57,6 +57,8 @@ Route::post('projects/{project_id}/captation-to-postproduction', 'App\Http\Controllers\StepController@captationToPostProd')->name('projects.captationToPostproduction'); Route::post('projects/{project_id}/validate/postproduction', 'App\Http\Controllers\StepController@validatePostProd')->name('projects.validatePostProd'); Route::post('projects/{project_id}/validate/transcription', 'App\Http\Controllers\StepController@validateTranscription')->name('projects.validateTranscription'); + Route::post('projects/{project_id}/publish', 'App\Http\Controllers\StepController@publish')->name('projects.publish'); + Route::post('projects/{project_id}/unpublish', 'App\Http\Controllers\StepController@unpublish')->name('projects.unpublish'); /****TEAM ****/ diff --git a/sera-back/storage/api-docs/api-docs.json b/sera-back/storage/api-docs/api-docs.json index abc8898..63db2e9 100644 --- a/sera-back/storage/api-docs/api-docs.json +++ b/sera-back/storage/api-docs/api-docs.json @@ -1504,7 +1504,7 @@ "type": "string", "enum": [ "ongoing", - "completed", + "published", "cancelled" ] }, @@ -4080,6 +4080,166 @@ } } }, + "/api/projects/{project_id}/publish": { + "post": { + "tags": [ + "Projects" + ], + "summary": "Publish project", + "description": "Publish project", + "operationId": "PublishProject", + "parameters": [ + { + "name": "project_id", + "in": "path", + "description": "Project id", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "Project published", + "content": { + "application/json": { + "schema": { + "properties": { + "id": { + "type": "integer", + "example": "1" + }, + "title": { + "type": "string", + "example": "Project title" + }, + "description": { + "type": "string", + "example": "Project description" + }, + "status": { + "type": "string", + "example": "published" + }, + "created_at": { + "type": "string", + "example": "2021-05-05T14:48:00.000000Z" + }, + "updated_at": { + "type": "string", + "example": "2021-05-05T14:48:00.000000Z" + }, + "project_request_id": { + "type": "integer", + "example": "1" + } + }, + "type": "object" + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "properties": { + "error": { + "type": "string", + "example": "Project has no subtitles." + } + }, + "type": "object" + } + } + } + } + } + } + }, + "/api/projects/{project_id}/unpublish": { + "post": { + "tags": [ + "Projects" + ], + "summary": "Unpublish project", + "description": "Unpublish project", + "operationId": "UnpublishProject", + "parameters": [ + { + "name": "project_id", + "in": "path", + "description": "Project id", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "Project unpublished", + "content": { + "application/json": { + "schema": { + "properties": { + "id": { + "type": "integer", + "example": "1" + }, + "title": { + "type": "string", + "example": "Project title" + }, + "description": { + "type": "string", + "example": "Project description" + }, + "status": { + "type": "string", + "example": "ongoing" + }, + "created_at": { + "type": "string", + "example": "2021-05-05T14:48:00.000000Z" + }, + "updated_at": { + "type": "string", + "example": "2021-05-05T14:48:00.000000Z" + }, + "project_request_id": { + "type": "integer", + "example": "1" + } + }, + "type": "object" + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "properties": { + "error": { + "type": "string", + "example": "Project is not published." + } + }, + "type": "object" + } + } + } + } + } + } + }, "/api/projects/{projectId}/subtitles": { "get": { "tags": [ From 3739c6ed6452a8c6a0b785413d5b219bf8f76e8a Mon Sep 17 00:00:00 2001 From: Adrien Albuquerque Date: Fri, 15 Sep 2023 00:03:14 +0200 Subject: [PATCH 03/27] =?UTF-8?q?feat(ApiKey):=20Create/Recreate/Destroy/G?= =?UTF-8?q?ets=20(=E2=9D=81=C2=B4=E2=97=A1`=E2=9D=81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/Http/Controllers/ApiKeyController.php | 194 ++++++++++++++ sera-back/app/Models/ApiKey.php | 12 + sera-back/config/roles.php | 3 +- ...023_09_14_213435_create_api_keys_table.php | 32 +++ sera-back/routes/api.php | 6 + sera-back/storage/api-docs/api-docs.json | 240 ++++++++++++++++++ 6 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 sera-back/app/Http/Controllers/ApiKeyController.php create mode 100644 sera-back/app/Models/ApiKey.php create mode 100644 sera-back/database/migrations/2023_09_14_213435_create_api_keys_table.php diff --git a/sera-back/app/Http/Controllers/ApiKeyController.php b/sera-back/app/Http/Controllers/ApiKeyController.php new file mode 100644 index 0000000..ed929df --- /dev/null +++ b/sera-back/app/Http/Controllers/ApiKeyController.php @@ -0,0 +1,194 @@ +validate([ + 'name' => 'required|string', + 'description' => 'nullable|string', + 'expires_at' => 'required|date', + 'never_expires' => 'required|boolean', + ]); + + $apiKey = new ApiKey; + $apiKey->user_id = $request->user()->id; + $apiKey->key = bin2hex(random_bytes(32)); + $apiKey->name = $validated['name']; + $apiKey->description = $validated['description']; + $apiKey->expires_at = $validated['never_expires'] ? null : $validated['expires_at']; + $apiKey->save(); + + return response()->json([ + 'message' => 'API key created successfully', + 'data' => $apiKey, + ], 201); + } + + /** + * @OA\Post( + * path="/api/api-keys/{apikey_id}/recreate", + * tags={"API Key"}, + * summary="Recreate an API key", + * description="Recreate an API key", + * @OA\Parameter( + * name="apikey_id", + * in="path", + * description="ID of the API key to recreate", + * required=true, + * @OA\Schema( + * type="integer", + * format="int64" + * ) + * ), + * @OA\Response( + * response=200, + * description="API key recreated successfully", + * @OA\JsonContent( + * @OA\Property(property="message", type="string", example="API key recreated successfully"), + * ) + * ), + * @OA\Response( + * response=404, + * description="API key not found", + * @OA\JsonContent( + * @OA\Property(property="message", type="string", example="API key not found"), + * ) + * ), + * ) + */ + public function recreate($apikey_id) + { + $apiKey = ApiKey::find($apikey_id); + + if (!$apiKey) { + return response()->json([ + 'message' => 'API key not found', + ], 404); + } + + $apiKey->key = bin2hex(random_bytes(32)); + $apiKey->save(); + + return response()->json([ + 'message' => 'API key recreated successfully', + 'data' => $apiKey, + ], 200); + } + + /** + * @OA\Delete( + * path="/api/api-keys/{apikey_id}", + * tags={"API Key"}, + * summary="Delete an API key", + * description="Delete an API key", + * @OA\Parameter( + * name="apikey_id", + * in="path", + * description="ID of the API key to delete", + * required=true, + * @OA\Schema( + * type="integer", + * format="int64" + * ) + * ), + * @OA\Response( + * response=200, + * description="API key deleted successfully", + * @OA\JsonContent( + * @OA\Property(property="message", type="string", example="API key deleted successfully"), + * ) + * ), + * @OA\Response( + * response=404, + * description="API key not found", + * @OA\JsonContent( + * @OA\Property(property="message", type="string", example="API key not found"), + * ) + * ), + * ) + */ + public function destroy($apikey_id) + { + $apiKey = ApiKey::find($apikey_id); + + if (!$apiKey) { + return response()->json([ + 'message' => 'API key not found', + ], 404); + } + + $apiKey->delete(); + + return response()->json([ + 'message' => 'API key deleted successfully', + ], 200); + } + + /** + * @OA\Get( + * path="/api/api-keys", + * tags={"API Key"}, + * summary="Get all API keys", + * description="Get all API keys", + * @OA\Response( + * response=200, + * description="API keys retrieved successfully", + * @OA\JsonContent( + * @OA\Property(property="message", type="string", example="API keys retrieved successfully"), + * ) + * ), + * ) + */ + public function index(){ + + return response()->json(ApiKey::all(), 200); + } +} diff --git a/sera-back/app/Models/ApiKey.php b/sera-back/app/Models/ApiKey.php new file mode 100644 index 0000000..e9ebf90 --- /dev/null +++ b/sera-back/app/Models/ApiKey.php @@ -0,0 +1,12 @@ + ["store","destroy","index"], "knowledges" => ["show", "index", "store", "update", "destroy", "getTypes"], "edito" => ["show", "index", "store", "update", "destroy","removeImage","addKnowledge","removeKnowledge"], + "api-keys" => ["store", "recreate", "destroy","index"], ], "project_manager" => [ "projects-requests" => ["show", "index", "update"], @@ -30,7 +31,7 @@ "subtitles" => ["store","destroy","index"], "knowledges" => ["show", "index", "store", "update", "destroy", "getTypes"], "edito" => ["show", "index", "store", "update", "destroy","removeImage","addKnowledge","removeKnowledge"], - + "api-keys" => ["store", "recreate", "destroy","index"], ], "professor" => [ "users" => ["iso","show", "index", "roles", "image", "password","reservations"], diff --git a/sera-back/database/migrations/2023_09_14_213435_create_api_keys_table.php b/sera-back/database/migrations/2023_09_14_213435_create_api_keys_table.php new file mode 100644 index 0000000..d8abb5f --- /dev/null +++ b/sera-back/database/migrations/2023_09_14_213435_create_api_keys_table.php @@ -0,0 +1,32 @@ +id(); + $table->foreignId('user_id')->constrained()->onDelete('cascade'); + $table->string('key'); + $table->string('name'); + $table->text('description')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('api_keys'); + } +}; diff --git a/sera-back/routes/api.php b/sera-back/routes/api.php index 94dd3bf..7e7ca75 100644 --- a/sera-back/routes/api.php +++ b/sera-back/routes/api.php @@ -136,6 +136,12 @@ Route::post('projects/{projectId}/edito/remove-knowledge', 'App\Http\Controllers\EditoController@unlinkKnowledge')->name('edito.removeKnowledge'); /************************/ + /***** API Key *****/ + Route::post('api-keys', 'App\Http\Controllers\ApiKeyController@store')->name('api-keys.store'); + Route::post('api-keys/{apikey_id}/recreate', 'App\Http\Controllers\ApiKeyController@recreate')->name('api-keys.recreate'); + Route::delete('api-keys/{apikey_id}', 'App\Http\Controllers\ApiKeyController@destroy')->name('api-keys.destroy'); + Route::get('api-keys', 'App\Http\Controllers\ApiKeyController@index')->name('api-keys.index'); + /*********************************/ diff --git a/sera-back/storage/api-docs/api-docs.json b/sera-back/storage/api-docs/api-docs.json index 63db2e9..c481185 100644 --- a/sera-back/storage/api-docs/api-docs.json +++ b/sera-back/storage/api-docs/api-docs.json @@ -5,6 +5,246 @@ "version": "1.0.0" }, "paths": { + "/api/api-keys": { + "get": { + "tags": [ + "API Key" + ], + "summary": "Get all API keys", + "description": "Get all API keys", + "operationId": "48d6f21fc4f70c87bb9e0fe128fc333d", + "responses": { + "200": { + "description": "API keys retrieved successfully", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "example": "API keys retrieved successfully" + } + }, + "type": "object" + } + } + } + } + } + }, + "post": { + "tags": [ + "API Key" + ], + "summary": "Create an API key", + "description": "Create an API key", + "operationId": "97fc3d21663312cdba253bcfe2bd244e", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "required": [ + "name", + "expires_at", + "never_expires" + ], + "properties": { + "name": { + "type": "string", + "example": "My API key" + }, + "description": { + "type": "string", + "example": "My API key description" + }, + "expires_at": { + "type": "string", + "format": "date-time", + "example": "2021-09-14T21:34:35.000000Z" + }, + "never_expires": { + "type": "boolean", + "example": "false" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "201": { + "description": "API key created successfully", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "example": "API key created successfully" + } + }, + "type": "object" + } + } + } + }, + "400": { + "description": "Validation error", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "example": "The given data was invalid." + }, + "errors": { + "type": "object", + "example": { + "name": [ + "The name field is required." + ] + } + } + }, + "type": "object" + } + } + } + }, + "401": { + "description": "Unauthenticated", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "example": "Unauthenticated." + } + }, + "type": "object" + } + } + } + } + } + } + }, + "/api/api-keys/{apikey_id}/recreate": { + "post": { + "tags": [ + "API Key" + ], + "summary": "Recreate an API key", + "description": "Recreate an API key", + "operationId": "c766b71c349f65f13633900bf2435acf", + "parameters": [ + { + "name": "apikey_id", + "in": "path", + "description": "ID of the API key to recreate", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "API key recreated successfully", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "example": "API key recreated successfully" + } + }, + "type": "object" + } + } + } + }, + "404": { + "description": "API key not found", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "example": "API key not found" + } + }, + "type": "object" + } + } + } + } + } + } + }, + "/api/api-keys/{apikey_id}": { + "delete": { + "tags": [ + "API Key" + ], + "summary": "Delete an API key", + "description": "Delete an API key", + "operationId": "735a9edea689ca45c188b2bbbf3dbac8", + "parameters": [ + { + "name": "apikey_id", + "in": "path", + "description": "ID of the API key to delete", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "API key deleted successfully", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "example": "API key deleted successfully" + } + }, + "type": "object" + } + } + } + }, + "404": { + "description": "API key not found", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "example": "API key not found" + } + }, + "type": "object" + } + } + } + } + } + } + }, "/api/projects/{project_id}/edito": { "get": { "tags": [ From 3c2fa81aa7ed00600109ea8d197b4c5abe8aea77 Mon Sep 17 00:00:00 2001 From: Adrien Albuquerque Date: Fri, 15 Sep 2023 00:46:38 +0200 Subject: [PATCH 04/27] feat(Course): One api to get all course + one other to show one api with a good format UwU --- .../app/Http/Controllers/CourseController.php | 77 +++++++++++++++++++ sera-back/routes/web.php | 3 + 2 files changed, 80 insertions(+) create mode 100644 sera-back/app/Http/Controllers/CourseController.php diff --git a/sera-back/app/Http/Controllers/CourseController.php b/sera-back/app/Http/Controllers/CourseController.php new file mode 100644 index 0000000..f3ef98c --- /dev/null +++ b/sera-back/app/Http/Controllers/CourseController.php @@ -0,0 +1,77 @@ +select('id', 'title', 'description') + ->paginate(15); + + return response()->json( + $projects, + 200 + ); + } + + public function show($id) + { + $project = Project::with(['videoReviews' => function ($query) { + $query->where('validated', true); + }, 'videoReviews.ressource', 'subtitles.ressource', 'edito.knowledges'])->find($id); + + if (!$project) { + return response()->json(['error' => 'Projet introuvable'], 404); + } + + $json = [ + "Title" => $project->title, + "Description" => $project->description, + "Video" => null, + "Subtitles" => [], + "Edito" => null, + ]; + + if ($project->videoReviews) { + $video = $project->videoReviews->first(); + $videoRessource = $video->ressource; + $json["Video"] = [ + "Name" => $videoRessource->name, + "Url" => $videoRessource->url, + "Type" => $videoRessource->type, + "Description" => $videoRessource->description, + ]; + } + + foreach ($project->subtitles as $subtitle) { + $subtitleRessource = $subtitle->ressource; + $json["Subtitles"][$subtitle->lang] = $subtitleRessource->url; + } + + if ($project->edito) { + $edito = $project->edito; + $editoJson = [ + "Title" => $edito->title, + "Description" => $edito->description, + "Knowledges" => $edito->knowledges->map(function ($knowledge) { + return [ + "Name" => $knowledge->name, + "Infos" => $knowledge->infos, + "Image" => $knowledge->image, + "Type" => $knowledge->type, + ]; + }), + "Images" => $edito->images, + ]; + $json["Edito"] = $editoJson; + } + + return response()->json($json, 200); + } + +} diff --git a/sera-back/routes/web.php b/sera-back/routes/web.php index 2a4a443..466f8df 100644 --- a/sera-back/routes/web.php +++ b/sera-back/routes/web.php @@ -18,6 +18,9 @@ }); +Route::get('/courses', 'App\Http\Controllers\CourseController@index')->name('courses.index'); +Route::get('/courses/{course}', 'App\Http\Controllers\CourseController@show')->name('courses.show'); + // Route::get('/test', 'App\Http\Controllers\UserController@test')->name('test'); require __DIR__.'/auth.php'; From 6026fd5ac40c31e56ee9d7ad2f2dd639d95bf8c4 Mon Sep 17 00:00:00 2001 From: Adrien Albuquerque Date: Fri, 15 Sep 2023 00:56:48 +0200 Subject: [PATCH 05/27] feat(Course): Middleware to check the api_key before request ~_~ --- sera-back/app/Http/Middleware/ApiCheck.php | 36 ++++++++++++++++++++++ sera-back/app/Models/ApiKey.php | 1 + sera-back/routes/web.php | 5 +-- 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 sera-back/app/Http/Middleware/ApiCheck.php diff --git a/sera-back/app/Http/Middleware/ApiCheck.php b/sera-back/app/Http/Middleware/ApiCheck.php new file mode 100644 index 0000000..c9b7aec --- /dev/null +++ b/sera-back/app/Http/Middleware/ApiCheck.php @@ -0,0 +1,36 @@ +header('api_token'); + // on vérifie que le token existe + if (!$token) { + // si le token n'existe pas, on retourne une erreur 401 + return response()->json(['error' => 'Unauthorized, no token provided'], 401); + } + // on vérifie que le token existe dans la table api_keys + $apiKey = ApiKey::where('key', $token)->first(); + + if (!$apiKey) { + // si le token n'existe pas, on retourne une erreur 401 + return response()->json(['error' => 'Unauthorized, invalid token'], 401); + } + + return $next($request); + } +} diff --git a/sera-back/app/Models/ApiKey.php b/sera-back/app/Models/ApiKey.php index e9ebf90..4906202 100644 --- a/sera-back/app/Models/ApiKey.php +++ b/sera-back/app/Models/ApiKey.php @@ -9,4 +9,5 @@ class ApiKey extends Model { use HasFactory; + } diff --git a/sera-back/routes/web.php b/sera-back/routes/web.php index 466f8df..46b1831 100644 --- a/sera-back/routes/web.php +++ b/sera-back/routes/web.php @@ -1,5 +1,6 @@ app()->version()]; }); +Route::get('/courses', 'App\Http\Controllers\CourseController@index')->name('courses.index')->middleware(ApiCheck::class); +Route::get('/courses/{course}', 'App\Http\Controllers\CourseController@show')->name('courses.show')->middleware(ApiCheck::class); -Route::get('/courses', 'App\Http\Controllers\CourseController@index')->name('courses.index'); -Route::get('/courses/{course}', 'App\Http\Controllers\CourseController@show')->name('courses.show'); // Route::get('/test', 'App\Http\Controllers\UserController@test')->name('test'); From 01480b9896866c68a8b392e254d85240712b64ba Mon Sep 17 00:00:00 2001 From: Adrien Albuquerque Date: Fri, 15 Sep 2023 01:03:06 +0200 Subject: [PATCH 06/27] chore(Course): Doc + local env remove restriction --- .../app/Http/Controllers/CourseController.php | 48 +++++++++++++++++ sera-back/app/Http/Middleware/ApiCheck.php | 6 +++ sera-back/storage/api-docs/api-docs.json | 51 +++++++++++++++++++ 3 files changed, 105 insertions(+) diff --git a/sera-back/app/Http/Controllers/CourseController.php b/sera-back/app/Http/Controllers/CourseController.php index f3ef98c..013aa8a 100644 --- a/sera-back/app/Http/Controllers/CourseController.php +++ b/sera-back/app/Http/Controllers/CourseController.php @@ -7,6 +7,23 @@ class CourseController extends Controller { + /** + * @OA\Get( + * path="/courses", + * operationId="getCourseList", + * tags={"Courses"}, + * summary="Get list of courses", + * description="Returns list of courses", + * @OA\Response( + * response=200, + * description="successful operation", + * ), + * @OA\Response( + * response=401, + * description="Unauthorized", + * ), + * ) + */ public function index() { $projects = Project::where('status', 'published') @@ -19,6 +36,37 @@ public function index() ); } + /** + * @OA\Get( + * path="/courses/{id}", + * operationId="getCourseById", + * tags={"Courses"}, + * summary="Get course information", + * description="Returns course data", + * @OA\Parameter( + * name="id", + * description="Course id", + * required=true, + * in="path", + * @OA\Schema( + * type="integer", + * format="int64" + * ) + * ), + * @OA\Response( + * response=200, + * description="successful operation", + * ), + * @OA\Response( + * response=401, + * description="Unauthorized", + * ), + * @OA\Response( + * response=404, + * description="Not found", + * ), + * ) + */ public function show($id) { $project = Project::with(['videoReviews' => function ($query) { diff --git a/sera-back/app/Http/Middleware/ApiCheck.php b/sera-back/app/Http/Middleware/ApiCheck.php index c9b7aec..06a3780 100644 --- a/sera-back/app/Http/Middleware/ApiCheck.php +++ b/sera-back/app/Http/Middleware/ApiCheck.php @@ -16,6 +16,12 @@ class ApiCheck */ public function handle(Request $request, Closure $next): Response { + + // si l'appel est fait en local, on ne vérifie pas le token + if (env('APP_ENV') === 'local') { + return $next($request); + } + // on récupère le token appelé "api_token" dans le header de la requête $token = $request->header('api_token'); // on vérifie que le token existe diff --git a/sera-back/storage/api-docs/api-docs.json b/sera-back/storage/api-docs/api-docs.json index c481185..e7ebb76 100644 --- a/sera-back/storage/api-docs/api-docs.json +++ b/sera-back/storage/api-docs/api-docs.json @@ -245,6 +245,57 @@ } } }, + "/courses": { + "get": { + "tags": [ + "Courses" + ], + "summary": "Get list of courses", + "description": "Returns list of courses", + "operationId": "getCourseList", + "responses": { + "200": { + "description": "successful operation" + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/courses/{id}": { + "get": { + "tags": [ + "Courses" + ], + "summary": "Get course information", + "description": "Returns course data", + "operationId": "getCourseById", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Course id", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Not found" + } + } + } + }, "/api/projects/{project_id}/edito": { "get": { "tags": [ From 10cc220a1eae57522253fd8415dfc9e493fd55b6 Mon Sep 17 00:00:00 2001 From: Adrien Albuquerque Date: Fri, 15 Sep 2023 01:30:56 +0200 Subject: [PATCH 07/27] feat(ApiKey): Seeders for each cursus director --- .../database/factories/ApiKeyFactory.php | 25 ++++++++++++++++++ sera-back/database/seeders/ApiKey.php | 26 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 sera-back/database/factories/ApiKeyFactory.php create mode 100644 sera-back/database/seeders/ApiKey.php diff --git a/sera-back/database/factories/ApiKeyFactory.php b/sera-back/database/factories/ApiKeyFactory.php new file mode 100644 index 0000000..ec9ec85 --- /dev/null +++ b/sera-back/database/factories/ApiKeyFactory.php @@ -0,0 +1,25 @@ + + */ +class ApiKeyFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'key' => $this->faker->uuid(), + 'name' => $this->faker->word(), + 'description' => $this->faker->sentence(), + ]; + } +} diff --git a/sera-back/database/seeders/ApiKey.php b/sera-back/database/seeders/ApiKey.php new file mode 100644 index 0000000..488236a --- /dev/null +++ b/sera-back/database/seeders/ApiKey.php @@ -0,0 +1,26 @@ +get(); + foreach ($users as $user) { + \App\Models\ApiKey::factory()->count(1)->create([ + 'user_id' => $user->id, + ]); + } + + + + } +} From 16ab83f3531ec51e6e141c5224ce14a128f42193 Mon Sep 17 00:00:00 2001 From: Adrien Albuquerque Date: Fri, 15 Sep 2023 01:32:55 +0200 Subject: [PATCH 08/27] =?UTF-8?q?feat:=20Add=20api=20to=20seeders=20?= =?UTF-8?q?=E2=98=86*:=20.=EF=BD=A1.=20o(=E2=89=A7=E2=96=BD=E2=89=A6)o=20.?= =?UTF-8?q?=EF=BD=A1.:*=E2=98=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sera-back/database/seeders/DatabaseSeeder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/sera-back/database/seeders/DatabaseSeeder.php b/sera-back/database/seeders/DatabaseSeeder.php index 8bf18a8..5006890 100644 --- a/sera-back/database/seeders/DatabaseSeeder.php +++ b/sera-back/database/seeders/DatabaseSeeder.php @@ -26,6 +26,7 @@ public function run(): void // VideoReviewSeeder::class, NotificationSeeder::class, KnowledgeSeeder::class, + ApiKey::class, ]); } } From cdf76dec05a879164f2e6a792345783effb46ccf Mon Sep 17 00:00:00 2001 From: Adrien Albuquerque Date: Fri, 15 Sep 2023 01:50:46 +0200 Subject: [PATCH 09/27] fix: sedder project8 for edito part UwU --- sera-back/database/seeders/ProjectSeeder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/sera-back/database/seeders/ProjectSeeder.php b/sera-back/database/seeders/ProjectSeeder.php index e8f803a..f8a5e15 100644 --- a/sera-back/database/seeders/ProjectSeeder.php +++ b/sera-back/database/seeders/ProjectSeeder.php @@ -231,6 +231,7 @@ public function run(): void $project->steps->{'Post-Production'}->status = 'done'; $project->steps->{'Transcription'}->status = 'done'; $project->steps->{'Subtitling'}->status = 'ongoing'; + $project->steps->{'Editorial'}->status = 'ongoing'; $project->steps = json_encode($project->steps); $project->save(); From f243b4c4f4b61e64997bf1a056e7e5a3ecc8ceae Mon Sep 17 00:00:00 2001 From: Adrien Albuquerque Date: Fri, 15 Sep 2023 01:46:33 +0200 Subject: [PATCH 10/27] =?UTF-8?q?fix(ApiKey):=20Remove=20key=20from=20inde?= =?UTF-8?q?x=20response=20(=F0=9F=91=89=EF=BE=9F=E3=83=AE=EF=BE=9F)?= =?UTF-8?q?=F0=9F=91=89=F0=9F=91=88(=EF=BE=9F=E3=83=AE=EF=BE=9F?= =?UTF-8?q?=F0=9F=91=88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sera-back/app/Http/Controllers/ApiKeyController.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sera-back/app/Http/Controllers/ApiKeyController.php b/sera-back/app/Http/Controllers/ApiKeyController.php index ed929df..0c378f1 100644 --- a/sera-back/app/Http/Controllers/ApiKeyController.php +++ b/sera-back/app/Http/Controllers/ApiKeyController.php @@ -188,7 +188,8 @@ public function destroy($apikey_id) * ) */ public function index(){ - - return response()->json(ApiKey::all(), 200); + // get all api keys but remove key from the response + $apiKeys = ApiKey::all()->makeHidden('key'); + return response()->json($apiKeys, 200); } } From 19c20c654972835c51c92c6ea19ce459fb99ef49 Mon Sep 17 00:00:00 2001 From: Antoine Azevedo Da Silva Date: Thu, 14 Sep 2023 20:19:11 +0200 Subject: [PATCH 11/27] feat(apikeys): create ApiKey page - create routes - restriction access - The page & modals --- .../src/components/app/apiKey/ApiKeyModal.tsx | 79 +++++++++++++++++++ .../src/components/app/apiKey/ApiKeyTable.tsx | 69 ++++++++++++++++ .../src/components/app/navigation/Nav.tsx | 19 +++++ .../Reservation/ReservationContainer.tsx | 43 +--------- sera-front/src/components/ui/datePicker.tsx | 41 ++++++++++ sera-front/src/lib/routes.tsx | 5 ++ sera-front/src/lib/utils.ts | 23 ++---- sera-front/src/pages/ApiKey.tsx | 68 ++++++++++++++++ 8 files changed, 291 insertions(+), 56 deletions(-) create mode 100644 sera-front/src/components/app/apiKey/ApiKeyModal.tsx create mode 100644 sera-front/src/components/app/apiKey/ApiKeyTable.tsx create mode 100644 sera-front/src/components/ui/datePicker.tsx create mode 100644 sera-front/src/pages/ApiKey.tsx diff --git a/sera-front/src/components/app/apiKey/ApiKeyModal.tsx b/sera-front/src/components/app/apiKey/ApiKeyModal.tsx new file mode 100644 index 0000000..56d40b0 --- /dev/null +++ b/sera-front/src/components/app/apiKey/ApiKeyModal.tsx @@ -0,0 +1,79 @@ +import { useState } from "react"; + +import { Button } from "@/components/ui/button"; +import { DatePicker } from "@/components/ui/datePicker"; +import { + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; + +export const AddKeyDialog = () => { + const [apiKey, setApiKey] = useState(""); + const [keyName, setKeyName] = useState(""); + const [keyDescription, setKeyDescription] = useState(""); + const [keyExpiration, setKeyExpiration] = useState(); + + if (apiKey && apiKey != "") { + return <>; + } + + return ( + + +

+ Create a new API key +

+
+ + Generate a new API key to use with the Sera API. + +
+
+ + +
+
+ + setKeyName(e.target.value)} + /> +
+
+ + setKeyDescription(e.target.value)} + /> +
+
+ + + +
+ ); +}; diff --git a/sera-front/src/components/app/apiKey/ApiKeyTable.tsx b/sera-front/src/components/app/apiKey/ApiKeyTable.tsx new file mode 100644 index 0000000..95bc193 --- /dev/null +++ b/sera-front/src/components/app/apiKey/ApiKeyTable.tsx @@ -0,0 +1,69 @@ +import { TrashIcon } from "lucide-react"; + +import { Button } from "@/components/ui/button"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { convertDateFromDateType } from "@/lib/utils"; + +export const ApiKeyTable = ({ data }: { data: any }) => { + return ( + + + + +

API Key name

+
+ +

API Key description

+
+ +

API Key expiration

+
+ +

Action

+
+
+
+ + {console.log(data, data.length, data.length > 0)} + {data && + data.length > 0 && + data.map((keyElement: any) => { + return ( + + +

{keyElement.name}

+
+ +

{keyElement.description}

+
+ +

{convertDateFromDateType(keyElement.expiration)}

+
+ + + +
+ ); + })} +
+
+ ); +}; diff --git a/sera-front/src/components/app/navigation/Nav.tsx b/sera-front/src/components/app/navigation/Nav.tsx index 81d8f19..d0633cc 100644 --- a/sera-front/src/components/app/navigation/Nav.tsx +++ b/sera-front/src/components/app/navigation/Nav.tsx @@ -4,6 +4,7 @@ import { DoorOpen, FolderOpen, Home, + KeyRound, Ticket, User, WalletCards, @@ -144,6 +145,24 @@ export const Nav = () => { Projects

+ + {accessManager("api", undefined) && ( + + +

+ API Keys +

+ + )} ); diff --git a/sera-front/src/components/app/project/Reservation/ReservationContainer.tsx b/sera-front/src/components/app/project/Reservation/ReservationContainer.tsx index 6f1241b..552db9a 100644 --- a/sera-front/src/components/app/project/Reservation/ReservationContainer.tsx +++ b/sera-front/src/components/app/project/Reservation/ReservationContainer.tsx @@ -3,7 +3,6 @@ import clsx from "clsx"; import { ArrowLeft, ArrowRight, - CalendarIcon, Clapperboard, PenBox, Plus, @@ -13,7 +12,7 @@ import { useEffect, useState } from "react"; import { useParams } from "react-router-dom"; import { Button } from "@/components/ui/button"; -import { Calendar } from "@/components/ui/calendar"; +import { DatePicker } from "@/components/ui/datePicker"; import { Dialog, DialogContent, @@ -24,20 +23,9 @@ import { } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; import { Separator } from "@/components/ui/separator"; import { axios } from "@/lib/axios"; -import { - accessManager, - cn, - convertDate, - convertDateFromDateType, - selectRoleDisplay, -} from "@/lib/utils"; +import { accessManager, convertDate, selectRoleDisplay } from "@/lib/utils"; import { BigLoader } from "@/pages/skeletons/BigLoader"; export const ReservationContainer = ({ @@ -516,30 +504,3 @@ const SearchRoomDialog = ({ ); }; - -const DatePicker = ({ date, setDate }: any) => { - return ( - - - - - - - - - ); -}; diff --git a/sera-front/src/components/ui/datePicker.tsx b/sera-front/src/components/ui/datePicker.tsx new file mode 100644 index 0000000..13c467b --- /dev/null +++ b/sera-front/src/components/ui/datePicker.tsx @@ -0,0 +1,41 @@ +import { CalendarIcon } from "lucide-react"; + +import { cn, convertDateFromDateType } from "@/lib/utils"; + +import { Button } from "./button"; +import { Calendar } from "./calendar"; +import { Popover, PopoverContent, PopoverTrigger } from "./popover"; + +export const DatePicker = ({ date, setDate, hideToday = false }: any) => { + const today = new Date(); + + if (hideToday) { + today.setDate(today.getDate() + 1); + } + + return ( + + + + + + + + + ); +}; diff --git a/sera-front/src/lib/routes.tsx b/sera-front/src/lib/routes.tsx index f59aa3c..6b3ac42 100644 --- a/sera-front/src/lib/routes.tsx +++ b/sera-front/src/lib/routes.tsx @@ -1,5 +1,6 @@ import { createBrowserRouter, Navigate } from "react-router-dom"; +import { ApiKey } from "@/pages/ApiKey"; import App from "@/pages/App"; import { Capture } from "@/pages/Capture"; import { Error404 } from "@/pages/Error404"; @@ -28,6 +29,10 @@ const paths = [ index: true, element: , }, + { + path: "api", + element: , + }, { path: "rooms", element: , diff --git a/sera-front/src/lib/utils.ts b/sera-front/src/lib/utils.ts index 6417ac3..aecf7b6 100644 --- a/sera-front/src/lib/utils.ts +++ b/sera-front/src/lib/utils.ts @@ -253,26 +253,23 @@ export const accessManager = (page?: string, action?: string) => { if (role === "cursus_director") return true; if (role === "project_manager") { - //Project request + if (page === "api") return false; + if (action === "add_project_request") return false; if (action === "delete_project_request") return false; - - //Project if (action === "validate_project_step") return true; - - //Rooms if (action === "add_room") return false; if (action === "edit_room") return false; if (action === "delete_room") return false; } if (role === "professor") { + if (page === "api") return false; if (page === "users") return false; if (page === "rooms") return false; if (page === "project_requests") return false; if (page === "knowledge_base") return false; - //Project if (action === "validate_project_step") return false; if (action === "remove_project_team_member") return false; if (action === "add_project_team_member") return false; @@ -287,12 +284,12 @@ export const accessManager = (page?: string, action?: string) => { } if (role === "video_team") { + if (page === "api") return false; if (page === "users") return false; if (page === "rooms") return false; if (page === "project_requests") return false; if (page === "knowledge_base") return false; - //Project if (action === "validate_project_step") return false; if (action === "remove_project_team_member") return false; if (action === "add_project_team_member") return false; @@ -307,12 +304,12 @@ export const accessManager = (page?: string, action?: string) => { } if (role === "video_editor") { + if (page === "api") return false; if (page === "users") return false; if (page === "rooms") return false; if (page === "project_requests") return false; if (page === "knowledge_base") return false; - //Project if (action === "validate_project_step") return false; if (action === "remove_project_team_member") return false; if (action === "add_project_team_member") return false; @@ -327,12 +324,12 @@ export const accessManager = (page?: string, action?: string) => { } if (role === "transcription_team") { + if (page === "api") return false; if (page === "users") return false; if (page === "rooms") return false; if (page === "project_requests") return false; if (page === "knowledge_base") return false; - //Project if (action === "validate_project_step") return false; if (action === "remove_project_team_member") return false; if (action === "add_project_team_member") return false; @@ -342,17 +339,15 @@ export const accessManager = (page?: string, action?: string) => { if (action === "add_professor_notes") return false; if (action === "add_video_version") return false; if (action === "add_subs") return false; - - //Ajouter pr l'edito et les subs } if (role === "traduction_team") { + if (page === "api") return false; if (page === "users") return false; if (page === "rooms") return false; if (page === "project_requests") return false; if (page === "knowledge_base") return false; - //Project if (action === "validate_project_step") return false; if (action === "remove_project_team_member") return false; if (action === "add_project_team_member") return false; @@ -362,17 +357,15 @@ export const accessManager = (page?: string, action?: string) => { if (action === "add_professor_notes") return false; if (action === "add_video_version") return false; if (action === "add_transcript") return false; - - //Ajouter pr l'edito et les subs } if (role === "editorial_team") { + if (page === "api") return false; if (page === "users") return false; if (page === "rooms") return false; if (page === "project_requests") return false; if (page === "knowledge_base") return false; - //Project if (action === "validate_project_step") return false; if (action === "remove_project_team_member") return false; if (action === "add_project_team_member") return false; diff --git a/sera-front/src/pages/ApiKey.tsx b/sera-front/src/pages/ApiKey.tsx new file mode 100644 index 0000000..ca81666 --- /dev/null +++ b/sera-front/src/pages/ApiKey.tsx @@ -0,0 +1,68 @@ +import { useEffect, useState } from "react"; +import { useNavigate, useSearchParams } from "react-router-dom"; + +import { AddKeyDialog } from "@/components/app/apiKey/ApiKeyModal"; +import { ApiKeyTable } from "@/components/app/apiKey/ApiKeyTable"; +import { Button } from "@/components/ui/button"; +import { Dialog } from "@/components/ui/dialog"; +import { accessManager } from "@/lib/utils"; + +export const ApiKey = () => { + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + const [addApiKeyDialog, setAddApiKeyDialog] = useState(false); + + useEffect(() => { + if (!accessManager("api", undefined)) { + return navigate("/dashboard"); + } + + if (searchParams.get("action") || addApiKeyDialog) { + navigate("/dashboard/api?action=add"); + setAddApiKeyDialog(true); + } + }, [addApiKeyDialog]); + + return ( +
+
+

API Keys

+
+ {accessManager(undefined, "add_api_key") && ( + + )} +
+
+ + + + { + navigate("/dashboard/api"); + setAddApiKeyDialog(false); + }} + open={addApiKeyDialog} + > + + +
+ ); +}; + +const data = [ + { + name: "test", + description: "test", + expiration: new Date("2023-08-01"), + }, + { + name: "API key for the website 1", + description: "API key for the website 1", + expiration: new Date("2023-12-01"), + }, +]; From a358ef9b2dcc62847f2d651b85031d246480d13a Mon Sep 17 00:00:00 2001 From: Antoine Azevedo Da Silva Date: Thu, 14 Sep 2023 22:08:08 +0200 Subject: [PATCH 12/27] feat: added support action with specific key --- .../src/components/app/apiKey/ApiKeyTable.tsx | 21 ++++++++----------- sera-front/src/lib/routes.tsx | 6 ++++++ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/sera-front/src/components/app/apiKey/ApiKeyTable.tsx b/sera-front/src/components/app/apiKey/ApiKeyTable.tsx index 95bc193..b172f27 100644 --- a/sera-front/src/components/app/apiKey/ApiKeyTable.tsx +++ b/sera-front/src/components/app/apiKey/ApiKeyTable.tsx @@ -1,6 +1,6 @@ -import { TrashIcon } from "lucide-react"; +import { RefreshCw, Trash } from "lucide-react"; +import { Link } from "react-router-dom"; -import { Button } from "@/components/ui/button"; import { Table, TableBody, @@ -49,16 +49,13 @@ export const ApiKeyTable = ({ data }: { data: any }) => {

{convertDateFromDateType(keyElement.expiration)}

- - + + + + + + + ); diff --git a/sera-front/src/lib/routes.tsx b/sera-front/src/lib/routes.tsx index 6b3ac42..9338278 100644 --- a/sera-front/src/lib/routes.tsx +++ b/sera-front/src/lib/routes.tsx @@ -32,6 +32,12 @@ const paths = [ { path: "api", element: , + children: [ + { + path: ":keyId", + element: , + }, + ], }, { path: "rooms", From eacd94055d7933b98583115b4e9c3c34f94cc291 Mon Sep 17 00:00:00 2001 From: Antoine Azevedo Da Silva Date: Thu, 14 Sep 2023 22:42:16 +0200 Subject: [PATCH 13/27] fix: wrong key in map --- sera-front/src/components/app/apiKey/ApiKeyTable.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sera-front/src/components/app/apiKey/ApiKeyTable.tsx b/sera-front/src/components/app/apiKey/ApiKeyTable.tsx index b172f27..89a8737 100644 --- a/sera-front/src/components/app/apiKey/ApiKeyTable.tsx +++ b/sera-front/src/components/app/apiKey/ApiKeyTable.tsx @@ -31,13 +31,12 @@ export const ApiKeyTable = ({ data }: { data: any }) => { - {console.log(data, data.length, data.length > 0)} {data && data.length > 0 && - data.map((keyElement: any) => { + data.map((keyElement: any, index: number) => { return ( From 5aaacf15254db4fb2a11e7cb22a6b2a58c816dc1 Mon Sep 17 00:00:00 2001 From: Antoine Azevedo Da Silva Date: Thu, 14 Sep 2023 22:42:35 +0200 Subject: [PATCH 14/27] feat: create renew & delete modal --- .../src/components/app/apiKey/ApiKeyModal.tsx | 72 +++++++++++++++++-- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/sera-front/src/components/app/apiKey/ApiKeyModal.tsx b/sera-front/src/components/app/apiKey/ApiKeyModal.tsx index 56d40b0..56358af 100644 --- a/sera-front/src/components/app/apiKey/ApiKeyModal.tsx +++ b/sera-front/src/components/app/apiKey/ApiKeyModal.tsx @@ -7,6 +7,7 @@ import { DialogDescription, DialogFooter, DialogHeader, + DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; @@ -24,13 +25,14 @@ export const AddKeyDialog = () => { return ( -

+ Create a new API key -

+ + + Generate a new API key to use with the Sera API. +
- - Generate a new API key to use with the Sera API. - +