From db85d100d7e3e8ba53a65d3303eac8cd7913da98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20N=C3=BCsse?= Date: Mon, 2 Oct 2023 21:26:46 +0200 Subject: [PATCH] expose image api for other clients as api v1.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Felix NĂ¼sse --- appinfo/routes.php | 20 ++++++++- docs/api/v1.md | 61 ++++++++++++++++++++++++--- lib/AppInfo/Application.php | 2 +- lib/Controller/NotesApiController.php | 51 +++++++++++++++++++++- 4 files changed, 124 insertions(+), 10 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index b1a7cd8b6..edca117c5 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -177,7 +177,7 @@ 'url' => '/api/{apiVersion}/settings', 'verb' => 'GET', 'requirements' => [ - 'apiVersion' => '(v1)', + 'apiVersion' => '(v1|v1.4)', ], ], [ @@ -197,4 +197,22 @@ 'path' => '.+', ], ], + [ + 'name' => 'notes_api#getAttachment', + 'url' => '/api/{apiVersion}/attachment/{noteid}', + 'verb' => 'GET', + 'requirements' => [ + 'apiVersion' => '(v1.4)', + 'noteid' => '\d+' + ], + ], + [ + 'name' => 'notes_api#uploadFile', + 'url' => '/api/{apiVersion}/attachment/{noteid}', + 'verb' => 'POST', + 'requirements' => [ + 'apiVersion' => '(v1.4)', + 'noteid' => '\d+' + ], + ], ]]; diff --git a/docs/api/v1.md b/docs/api/v1.md index 8b652cbff..4bf9592b1 100644 --- a/docs/api/v1.md +++ b/docs/api/v1.md @@ -5,12 +5,13 @@ In this document, the Notes API major version 1 and all its minor versions are d ## Minor versions -| API version | Introduced with app version | Remarkable Changes | -|:-----------:|:----------------------------|:-------------------| -| **1.0** | Notes 3.3 (May 2020) | Separate title, no auto rename based on content | -| **1.1** | Notes 3.4 (May 2020) | Filter "Get all notes" by category | -| **1.2** | Notes 4.1 (June 2021) | Preventing lost updates, read-only notes, settings | -| **1.3** | Notes 4.5 (August 2022) | Allow custom file suffixes | +| API version | Introduced with app version | Remarkable Changes | +|:-----------:|:----------------------------|:---------------------------------------------------| +| **1.0** | Notes 3.3 (May 2020) | Separate title, no auto rename based on content | +| **1.1** | Notes 3.4 (May 2020) | Filter "Get all notes" by category | +| **1.2** | Notes 4.1 (June 2021) | Preventing lost updates, read-only notes, settings | +| **1.3** | Notes 4.5 (August 2022) | Allow custom file suffixes | +| **1.4** | Notes 4.9 (November 2023) | Add external image api | @@ -132,7 +133,7 @@ Note not found.
Details #### Request parameters -- **Body**: some or all "read/write" attributes (see section [Note attributes](#note-attributes)), example: +- **Body**: some or all "read/write" attributes (see section [Note attributes](#note-attributes)), example: ```js { "title": "New note", @@ -274,6 +275,52 @@ No valid authentication credentials supplied. +### Get attachment (`GET /attachment/{id}`) +
Details + +*(since API v1.4)* + +#### Request parameters +| Parameter | Type | Description | +|:----------|:------------------------|:-----------------------------------| +| `path` | string, required (path) | Path or name of the image to load. | + +#### Response +##### 200 OK +- **Body**: Image or File + +##### 400 Bad Request +Endpoint not supported by installed notes app version (requires API version 1.4). + +##### 401 Unauthorized +No valid authentication credentials supplied. +
+ + +### Put attachment (`POST /attachment/{id}`) +
Details + +*(since API v1.4)* + +#### Request parameters +None. + +#### Response +##### 200 OK +- **Body**: Filename in json encoded: +```js +{ + "filename": "image.jpg" +} +``` + +##### 400 Bad Request +Endpoint not supported by installed notes app version (requires API version 1.4). + +##### 401 Unauthorized +No valid authentication credentials supplied. +
+ ## Preventing lost updates and conflict solution While changing a note using a Notes client, the same note may be changed by another client. diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index dc13cfdfb..cca98376b 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -12,7 +12,7 @@ class Application extends App implements IBootstrap { public const APP_ID = 'notes'; - public static array $API_VERSIONS = [ '0.2', '1.3' ]; + public static array $API_VERSIONS = [ '0.2', '1.3', '1.4' ]; public function __construct(array $urlParams = []) { parent::__construct(self::APP_ID, $urlParams); diff --git a/lib/Controller/NotesApiController.php b/lib/Controller/NotesApiController.php index fe98a1c1d..d617d96c8 100644 --- a/lib/Controller/NotesApiController.php +++ b/lib/Controller/NotesApiController.php @@ -12,6 +12,8 @@ use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Http\StreamResponse; +use OCP\Files\IMimeTypeDetector; use OCP\IRequest; class NotesApiController extends ApiController { @@ -19,6 +21,7 @@ class NotesApiController extends ApiController { private MetaService $metaService; private SettingsService $settingsService; private Helper $helper; + private IMimeTypeDetector $mimeTypeDetector; public function __construct( string $AppName, @@ -26,13 +29,15 @@ public function __construct( NotesService $service, MetaService $metaService, SettingsService $settingsService, - Helper $helper + Helper $helper, + IMimeTypeDetector $mimeTypeDetector ) { parent::__construct($AppName, $request); $this->service = $service; $this->metaService = $metaService; $this->settingsService = $settingsService; $this->helper = $helper; + $this->mimeTypeDetector = $mimeTypeDetector; } @@ -253,4 +258,48 @@ public function fail() : JSONResponse { return new JSONResponse([], Http::STATUS_BAD_REQUEST); }); } + + + + /** + * With help from: https://github.com/nextcloud/cookbook + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + * @return JSONResponse|StreamResponse + */ + public function getAttachment(int $noteid, string $path): Http\Response { + try { + $targetimage = $this->service->getAttachment( + $this->helper->getUID(), + $noteid, + $path + ); + $response = new StreamResponse($targetimage->fopen('rb')); + $response->addHeader('Content-Disposition', 'attachment; filename="' . rawurldecode($targetimage->getName()) . '"'); + $response->addHeader('Content-Type', $this->mimeTypeDetector->getSecureMimeType($targetimage->getMimeType())); + $response->addHeader('Cache-Control', 'public, max-age=604800'); + return $response; + } catch (\Exception $e) { + $this->helper->logException($e); + return $this->helper->createErrorResponse($e, Http::STATUS_NOT_FOUND); + } + } + + /** + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + */ + public function uploadFile(int $noteid): JSONResponse { + $file = $this->request->getUploadedFile('file'); + return $this->helper->handleErrorResponse(function () use ($noteid, $file) { + return $this->service->createImage( + $this->helper->getUID(), + $noteid, + $file + ); + }); + } + }