From 0adf5357bf10cf729ee8025aac6bbbcb5b59b381 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Tue, 7 Jan 2025 21:07:48 +0100 Subject: [PATCH] lots of new RawR resource manipulation routes --- api/src/core/main.tsp | 51 ++++++++++++++ api/src/core/routes/rawr.tsp | 124 +++++++++++++++++++++++++++++++++-- 2 files changed, 171 insertions(+), 4 deletions(-) diff --git a/api/src/core/main.tsp b/api/src/core/main.tsp index 61132edb..a3b3bfaf 100644 --- a/api/src/core/main.tsp +++ b/api/src/core/main.tsp @@ -129,6 +129,31 @@ namespace models { * added if they were known to the server. An example file name might be * `2c851bfb6daffa944fa1723c7bd4d362ffbc9defe292f2daaf05e895989d179b.jxl`, referencing the file * which was hosted at `/.p2/core/resource/2c851bfb6daffa944fa1723c7bd4d362ffbc9defe292f2daaf05e895989d179b.jxl`. + * In addition, the folder `rawr` contains a file named `access_properties.p2al`. This JSON + * file contains a data structure mapping each resource ID to an access properties object. + * In particular, the file is structured as an array containing objects. Each object has a key which is equal + * to the resource ID of a resource in the `rawr` directory and a value which is an object + * representing the access properties. An example of the contents of this file is given below: + * +```json +[ + { + "2062a23e2a25b226ca4c546fec5ec06e0df9648281f45da8b5aaabebdf66cf4c.jxl": { + "private": false, + "allowlist": ["user1@example.com", "instance.example.com"], + "denylist": ["user2@example.com", "otherinstance@example.com"] + } + }, + { + "a9144379a161e1fcf6b07801b70db6d6c481933bd634fe2409eb713723ab1a0a": { + "private": true, + "allowlist": ["user1@example.com"], + "denylist": [] + } + } +] +``` + * * If the server where the data export was requested from is the actors' home server, the * archive will contain a folder `certs` and a file `crypt_certs.p2epk`. `certs` will contain all ID-Certs * the server has stored of the actor. The ID-Certs will be stored in @@ -138,4 +163,30 @@ namespace models { * JSON file. */ model P2Export {} + + /** + * `ResourceAccessProperties` define which actors may access an uploaded resource. Actors and + * entire instances can have access granted or revoked. + */ + model ResourceAccessProperties { + @doc("Whether the resource should be private by default. Private resources can only be accessed by the uploader and by instances and actors declared in the `allowlist`.") + private: boolean = false; + @doc("A list of actors and/or instances allowed to access this resource.") + @example(#["user_i_like@example.com", "instance.example.com"]) + allowlist?: string[]; + @doc("A list of actors and/or instances who cannot have access to this resource.") + @example(#["user_i_dislike@example.com", "other_instance.example.com"]) + denylist?: string[]; + } + + /** + * When querying the server for a list of resources uploaded by you, you can optionally request + * the resulting list to be sorted in a specific way. These are the four options you have. + */ + enum ResourceListSorting { + SizeAsc, + SizeDesc, + NewestFirst, + OldestFirst + } } diff --git a/api/src/core/routes/rawr.tsp b/api/src/core/routes/rawr.tsp index 9706b2fd..9465be14 100644 --- a/api/src/core/routes/rawr.tsp +++ b/api/src/core/routes/rawr.tsp @@ -28,15 +28,27 @@ namespace ResourceAddressingWithRelativeRoots { * @param rid: Resource Identifier - unique identifier for a resource. * @returns * - `200`: File found and retrieved. + * - `308`: URI root has changed. * - `401`: Server or resource requires authentication to access this endpoint. * - `403`: Server or resource not accessible for the actor making this request. * - `404`: Resource not found. */ op getResource(@path rid: string): { - @statusCode _ : 403 | 401 | 404; - } | { @statusCode _ : 200; @body body: File; + } | { + @statusCode _ : 308; + @header({name: "Location"}) location: url; + @body reason: "ROOT_CHANGED"; + } | { + @statusCode _ : 403; + @body reason: "ACCESS_FORBIDDEN" + } | { + @statusCode _ : 401; + @body reason: "NEEDS_AUTHENTICATION" + } | { + @statusCode _ : 404; + @body reason: "NOT_FOUND" }; } @@ -47,8 +59,112 @@ namespace ResourceAddressingWithRelativeRoots { @post @added(Version.`v1.0-alpha.1`) @summary("Upload RawR resource") - op postResource(@path rid: string, @header({name: "Content-Length"}) contentLength: uint64): { - // TODO + /** + * Upload a [RawR](https://docs.polyphony.chat/Protocol%20Specifications/core/#731-resource-addressing-with-relative-roots) + * resource to your home server. + * @param rid: The resource ID of the resource you would like to upload. + * @param resourceAccessProperties ResourceAccessProperties. See the corresponding schema definition for more information. + * @param contentLength: The size of the resource in bytes. + * @param file: The resource itself + * @returns + * - `204`: Upload successful. + * - `403`: Uploading forbidden. + * - `409`: RID already exists on this server. Choose a different RID. + * - `411`: `Content-Length` header not specified. + * - `413`: Resource too large. + * - `414`: RID too long. + */ + op postResource( + @path rid: string, + @header({name: "Content-Length"}) contentLength: uint64, + @query resourceAccessProperties: polyproto.core.models.ResourceAccessProperties, + @body file: File; + ): { + @statusCode _ : 403; + @body reason: "UPLOAD_FORBIDDEN" + } | { + @statusCode _ : 409; + @body reason: "DUPLICATE_RID" + } | { + @statusCode _ : 411; + @body reason: "LENGTH_REQUIRED" + } | { + @statusCode _ : 413; + @body body: { + reason: "TOO_LARGE", + @doc("The server may tell the client how much more content they are allowed to store, in bytes.") + remainingStorageBytes?: uint64; + } + } | { + @statusCode _ : 414; + reason: "RID_TOO_LONG", + charLimit: uint8 = 64; + } | { + @statusCode _ : 204; + @header({name: "Content-Length"}) contentLength: 0; + }; + + @route("/{rid}") + @delete + @added(Version.`v1.0-alpha.1`) + @summary("Delete RawR resource") + op deleteResource(@path rid: string): { + @statusCode _ : 204; + }; + + @route("/{rid}") + @put + @added(Version.`v1.0-alpha.1`) + @summary("Update RawR resource access properties") + /** + * Replace the access properties of a [RawR](https://docs.polyphony.chat/Protocol%20Specifications/core/#731-resource-addressing-with-relative-roots) + * resource with updated access properties. + * @param rid The resource ID of the resource which the access properties should be modified of. + * @param resourceAccessProperties ResourceAccessProperties. See the corresponding schema definition for more information. + */ + op modifyResource(@path rid: string, @body resourceAccessProperties: polyproto.core.models.ResourceAccessProperties): { + @header({name: "Content-Length"}) contentLength: 0; + @statusCode _ : 204; + }; + + @route("/{rid}/info/") + @get + @added(Version.`v1.0-alpha.1`) + @summary("Retrieve information about one of your RawR resources") + /** + * @param rid The resource ID of the resource which you'd like to query information of. + */ + op getResourceInfos(@path rid: string): { + @statusCode statusCode : 200; + @body body : { + resourceId: string, + size: uint64, + access: polyproto.core.models.ResourceAccessProperties + }[] + } | { + @statusCode statusCode : 404; + @body reason: "NOT_FOUND"; + }; + + @route("/resources") + @get + @added(Version.`v1.0-alpha.1`) + @summary("List your uploaded resources") + /** + * Query the server for a list of resources you've uploaded. + * @param limit Optional; How many results you'd like to retrieve at maximum. Defaults to `50`. + * @param sort Whether the list should be sorted in a specific way. Available options are `SizeAsc`, `SizeDesc`, `NewestFirst` and `OldestFirst`. + */ + op getResourceList(@query limit?: uint32 = 50, @query sort?: polyproto.core.models.ResourceListSorting): { + @statusCode statusCode : 200; + @body body : { + resourceId: string, + size: uint64, + access: polyproto.core.models.ResourceAccessProperties + }[] + } | { + @statusCode _ : 204; + @header({name: "Content-Length"}) contentLength: 0; }; }