diff --git a/markdown/dev/reference/backend/api/apikey/en.md b/markdown/dev/reference/backend/api/apikey/en.md deleted file mode 100644 index 7d1774fad91..00000000000 --- a/markdown/dev/reference/backend/api/apikey/en.md +++ /dev/null @@ -1,314 +0,0 @@ ---- -title: API Keys ---- - -Documentation for the REST API endpoints to create, read, or remove API keys. - - -The FreeSewing backend REST API supports authentication both with JSON Web -Tokens (JWT) as with API keys (KEY). This describes the endpoints that deal -with creating, reading, and removing API keys. For authentication details, -refer to [the section on authenticating to the -API](/reference/backend/api#authentication). - - -## Create a new API key - -Create a new API key. The API key will belong to the user who is authenticated -when making the call. Supported for both JWT and KEY authentication. - - -The response to this API call is the only time the secret will be -revealed. - - -### Endpoints - -| Method | Path | Description | Auth | -| ------ | ---- | ----------- | ---- | -| | `/apikey/jwt` | Create a new API key | _jwt_ | -| | `/apikey/key` | Create a new API key | _key_ | - -### Parameters - - -| Where | Variable | Type | Description | -| ----- | ------------- | -------- | ----------- | -| _body_ | `name` | `string` | Create a new API key. Endpoint for JWT authentication | -| _body_ | `level` | `number` | A privilege level from 0 to 8. | -| _body_ | `expiresIn` | `number` | body | The number of seconds until this key expires. | - - -Returns HTTP status code on success, if -the request is malformed, and on server error. - -| Value | Type | Description | -| ------------------- | -------- | ----------- | -| `result` | `string` | `created` on success, and `error` on error | -| `apikey.key` | `string` | The API key | -| `apikey.secret` | `string` | The API secret | -| `apikey.level` | `number` | The privilege level of the API key | -| `apikey.expiresAt` | `string` | A string representation of the moment the API key expires | -| `apikey.name` | `string` | The name of the API key | -| `apikey.userId` | `number` | The ID of the user who created the API key | - - - -### Example - - -```js -const apiKey = axios.post( - 'https://backend.freesewing.org/apikey/jwt', - { - name: 'My first API key', - level: 2, // Read only - expiresIn: 3600, // One hour - }, - { - headers: { - Authorization: `Bearer ${token}` - } - } -) -``` - - - -```json -{ - result: 'success', - apikey: { - key: '7ea12968-7758-40b6-8c73-75cc99be762b', - secret: '503d7adbdb3ec18ab27adfcd895d8b47a8d6bc8307d548500fbf9c05a5a8820e', - level: 3, - expiresAt: '2022-11-06T15:57:30.190Z', - name: 'My first API key', - userId: 61 - } -} -``` - - - -## Read an API key - -Reads an existing API key. Note that the API secret can only be retrieved at -the moment the API key is created. - - -You need the `admin` role to read API keys of other users - -### Endpoints - -| Method | Path | Description | Auth | -| ------ | ---- | ----------- | ---- | -| | `/apikey/:id/jwt` | Reads an API key | _jwt_ | -| | `/apikey/:id/key` | Reads an API key | _key_ | - -### Parameters - - -| Where | Variable | Type | Description | -| ----- | ----------- | -------- | ----------- | -| _url_ | `:id` | `string` | The `key` field of the API key | - - -Returns HTTP status code on success, if -the request is malformed, if the key is not found, -and on server error. - -| Value | Type | Description | -| ------------------- | -------- | ----------- | -| `result` | `string` | `success` on success, and `error` on error | -| `apikey.key` | `string` | The API key | -| `apikey.level` | `number` | The privilege level of the API key | -| `apikey.expiresAt` | `string` | A string representation of the moment the API key expires | -| `apikey.name` | `string` | The name of the API key | -| `apikey.userId` | `number` | The ID of the user who created the API key | - - - -### Example - - -```js -const keyInfo = axios.get( - 'https://backend.freesewing.org/apikey/7ea12968-7758-40b6-8c73-75cc99be762b/jwt', - { - headers: { - Authorization: `Bearer ${token}` - } - } -) -``` - - - -```json -{ - result: 'success', - apikey: { - key: '7ea12968-7758-40b6-8c73-75cc99be762b', - level: 3, - expiresAt: '2022-11-06T15:57:30.190Z', - name: 'My first API key', - userId: 61 - } -} -``` - - - -## Read the current API key - -Reads the API key with which the current request was authenticated. - -### Endpoints - -| Method | Path | Description | Auth | -| ------ | ---- | ----------- | ---- | -| | `/whoami/key` | Reads the current API key | _key_ | - -### Parameters - - -This endpoint takes no parameters - - -Returns status code `200` on success, `400` on if the request is malformed, -`404` if the key is not found, and `500` on server error. - -| Value | Type | Description | -| ------------------- | -------- | ----------- | -| `result` | `string` | `success` on success, and `error` on error | -| `apikey.key` | `string` | The API key | -| `apikey.level` | `number` | The privilege level of the API key | -| `apikey.expiresAt` | `string` | A string representation of the moment the API key expires | -| `apikey.name` | `string` | The name of the API key | -| `apikey.userId` | `number` | The ID of the user who created the API key | - - - -### Example - - -```js -const keyInfo = axios.get( - 'https://backend.freesewing.org/whoami/key', - { - auth: { - username: apikey.key, - password: apikey.secret, - } - } -) -``` - - - -```json -{ - result: 'success', - apikey: { - key: '7ea12968-7758-40b6-8c73-75cc99be762b', - level: 3, - expiresAt: '2022-11-06T15:57:30.190Z', - name: 'My first API key', - userId: 61 - } -} -``` - - - - -## Remove an API key - -Removes an existing API key. - - -You need the `admin` role to remove API keys of other users - - -### Endpoints - -| Method | Path | Description | Auth | -| ------ | ---- | ----------- | ---- | -| | `/apikey/:id/jwt` | Removes an API key | _jwt_ | -| | `/apikey/:id/key` | Removes an API key | _key_ | - -### Parameters - - -| Where | Variable | Type | Description | -| ----- | ----------- | -------- | ----------- | -| _url_ | `:id` | `string` | The `key` field of the API key | - - -Returns HTTP status code on success, if -the request is malformed, if the key is not found, -and on server error. - - -### Example - - -```js -const keyInfo = axios.get( - 'https://backend.freesewing.org/apikey/7ea12968-7758-40b6-8c73-75cc99be762b/jwt', - { - headers: { - Authorization: `Bearer ${token}` - } - } -) -``` - - -Status code (no content) does not come with a body - - - -## Notes - -The following is good to keep in mind when working with API keys: - -### This is not the authentication documentation - -The FreeSewing backend REST API supports authentication both with JSON Web -Tokens (JWT) as with API keys (KEY). - -This describes the endpoints that deal with creating, reading, and removing API -keys. For authentication details, refer to [the section on authenticating to -the API](/reference/backend/api#authentication). - -### API keys are immutable - -Once created, API keys cannot be updated. -You should remove them and re-create a new one if you want to make change. - -### API keys have an expiry - -API keys have an expiry date. The maximum validity for an API key is 1 year. - -### API keys have a permission level - -API keys have a permission level. You can never create an API key with a higher -permission level than your own permission level. - -### Circumstances that will trigger your API keys to be revoked - -As a precaution, all your API keys will be revoked when: - -- Your role is downgraded to a role with fewer privileges -- Your account is (b)locked -- You revoke your consent for FreeSewing to process your data - - -This is not an exhaustive list. For example, if we find your use of our API to -be excessive, we might also revoke your API keys to shield us from the -financial impact of your use of our API. - - - diff --git a/markdown/dev/reference/backend/api/apikeys/create/en.md b/markdown/dev/reference/backend/api/apikeys/create/en.md new file mode 100644 index 00000000000..eb5090de6f8 --- /dev/null +++ b/markdown/dev/reference/backend/api/apikeys/create/en.md @@ -0,0 +1,97 @@ +--- +title: Create an API key +--- + +Creates a new API key. An API key can be used to authenticate against the +backend API. + +## Access control + +- [Permission level](/reference/backend/api/rbac) `4` or higher is required to create an API key + +## Endpoints + +Creating a new API key is possible via these endpoints: + +| Method | Path | Authentication | +| --------: | :--- | :------------- | +| | `/apikeys/jwt` | [JSON Web Token](/reference/backend/api/authentication#jwt-authentication) | +| | `/apikeys/key` | [API Key & Secret](/reference/backend/api/authentication#key-authentication) | + +## Request body + +| Property | Type | Description | +| ----------: | :------- | :---------- | +| `name` | `string` | A name for the API key | +| `level` | `number` | A privilege level from 0 to 8. | +| `expiresIn` | `number` | The number of seconds until the API key expires | + +## Response status codes + +Possible status codes for these endpoints are: + +| Status code | Description | +| ----------: | :---------- | +| | success | +| | the request was malformed | +| | the request lacks authentication | +| | authentication failed | +| | server error | + + +If the status code is not the `error` property +in the response body should indicate the nature of the problem. + + +## Response body + + +##### Make sure to save the secret +The response body is the only time the API key's secret will be revealed. + + +| Value | Type | Description | +| ------------------- | -------- | ----------- | +| `result` | `string` | Either `success` or `error` | +| `error` | `string` | Will give info on the nature of the error. Only set if an error occured. | +| `apikey.key` | `string` | The API key | +| `apikey.secret` | `string` | The API secret | +| `apikey.level` | `number` | The privilege level of the API key | +| `apikey.expiresAt` | `string` | A string representation of the moment the API key expires | +| `apikey.name` | `string` | The name of the API key | +| `apikey.userId` | `number` | The ID of the user who created the API key | + + + +## Example request + +```js +const apiKey = axios.post( + 'https://backend.freesewing.org/apikeys/jwt', + { + name: 'My first API key', + level: 2, // Read only + expiresIn: 3600, // One hour + }, + { + headers: { + Authorization: `Bearer ${token}` + } + } +) +``` + +## Example response +```200.json +{ + "result": "success", + "apikey": { + "key": "7ea12968-7758-40b6-8c73-75cc99be762b", + "secret": "503d7adbdb3ec18ab27adfcd895d8b47a8d6bc8307d548500fbf9c05a5a8820e", + "level": 3, + "expiresAt": "2022-11-06T15:57:30.190Z", + "name": "My first API key", + "userId": 61 + } +} +``` diff --git a/markdown/dev/reference/backend/api/apikeys/delete/en.md b/markdown/dev/reference/backend/api/apikeys/delete/en.md new file mode 100644 index 00000000000..ddd2a76e625 --- /dev/null +++ b/markdown/dev/reference/backend/api/apikeys/delete/en.md @@ -0,0 +1,58 @@ +--- +title: Delete an API key +--- + +Deletes an existing API key. + +## Access control + +- [Permission level](/reference/backend/api/rbac) `4` or higher is required to delete an API key + +## Endpoints + +Deleting an API key is possible via these endpoints: + +| Method | Path | Authentication | +| --------: | :--- | :------------- | +| | `/apikeys/:id/jwt` | [JSON Web Token](/reference/backend/api/authentication#jwt-authentication) | +| | `/apikeys/:id/key` | [API Key & Secret](/reference/backend/api/authentication#key-authentication) | + +## Request url + +The url should contain the ID of the API key you wish to remove. +It replaces the `:id` placeholder in the [endpoints listed above](#endpoints). + +## Response status codes + +Possible status codes for these endpoints are: + +| Status code | Description | +| ----------: | :---------- | +| | success | +| | the request was malformed | +| | the request lacks authentication | +| | authentication failed | +| | server error | + +## Example request + +```js +await axios.delete( + 'https://backend.freesewing.org/apikeys/7ea12968-7758-40b6-8c73-75cc99be762b/jwt', + { + headers: { + Authorization: `Bearer ${token}` + } + } +) +``` + +## Example response + +```204.json +``` + +These endpoints return status code (no content) on +success, with no response body. + + diff --git a/markdown/dev/reference/backend/api/apikeys/en.md b/markdown/dev/reference/backend/api/apikeys/en.md new file mode 100644 index 00000000000..bd06b6f6acb --- /dev/null +++ b/markdown/dev/reference/backend/api/apikeys/en.md @@ -0,0 +1,53 @@ +--- +title: API Keys +--- + +API keys are a way to authenticate to the API with basic authentication. +They are intended to be used when interacting with the API in an automated +way such as from a script or a CI/CD pipeline. + +They are an alternative to JSON Web Tokens (JWT) which is typically used +to authenticate users in a browser session. + +The FreeSewing backend REST API supports authentication both with JSON Web +Tokens (JWT) as with API keys (KEY). This describes the endpoints that deal +with creating, reading, and removing API keys. For authentication details, +refer to [the section on +authenticating](/reference/backend/api#authentication). + +## Endpoints + + + +## Notes + +The following is good to keep in mind when working with API keys: + +### API keys are immutable + +Once created, API keys cannot be updated. +You should remove them and re-create a new one if you want to make a change. + +### API keys have an expiry + +API keys have an expiry date. The maximum validity for an API key is 1 year. + +### API keys have a permission level + +API keys have a permission level. You can never create an API key with a higher +permission level than your own permission level. + +### Circumstances that will trigger your API keys to be revoked + +As a precaution, all your API keys will be revoked when: + +- Your role is downgraded to a role with fewer privileges +- Your account is (b)locked +- You revoke your consent for FreeSewing to process your data + + +This is not an exhaustive list. For example, if we find your use of our API to +be excessive, we might also revoke your API keys to shield us from the +financial impact of your use of our API. + + diff --git a/markdown/dev/reference/backend/api/apikeys/read/en.md b/markdown/dev/reference/backend/api/apikeys/read/en.md new file mode 100644 index 00000000000..644270a9357 --- /dev/null +++ b/markdown/dev/reference/backend/api/apikeys/read/en.md @@ -0,0 +1,81 @@ +--- +title: Read an API key +--- + +Reads an existing API key. Note that the API secret can only be retrieved at +the moment the API key is created. + +## Access control + +- [Permission level](/reference/backend/api/rbac) `4` or higher is required to read an API key + +## Endpoints + +Reading an API key is possible via these endpoints: + +| Method | Path | Authentication | +| --------: | :--- | :------------- | +| | `/apikeys/:id/jwt` | [JSON Web Token](/reference/backend/api/authentication#jwt-authentication) | +| | `/apikeys/:id/key` | [API Key & Secret](/reference/backend/api/authentication#key-authentication) | + +## Request url + +The url should contain the ID of the API key you wish to remove. +It replaces the `:id` placeholder in the [endpoints listed above](#endpoints). + +## Response status codes + +Possible status codes for these endpoints are: + +| Status code | Description | +| ----------: | :---------- | +| | success | +| | the request was malformed | +| | the request lacks authentication | +| | authentication failed | +| | API key not found | +| | server error | + + +If the status code is not the `error` property +in the response body should indicate the nature of the problem. + + +## Response body + +| Value | Type | Description | +| ------------------- | -------- | ----------- | +| `result` | `string` | `success` on success, and `error` on error | +| `error` | `string` | Will give info on the nature of the error. Only set if an error occured. | +| `apikey.key` | `string` | The API key | +| `apikey.level` | `number` | The privilege level of the API key | +| `apikey.expiresAt` | `string` | A string representation of the moment the API key expires | +| `apikey.name` | `string` | The name of the API key | +| `apikey.userId` | `number` | The ID of the user who created the API key | + +## Example request + +```js +const keyInfo = await axios.get( + 'https://backend.freesewing.org/apikeys/7ea12968-7758-40b6-8c73-75cc99be762b/jwt', + { + headers: { + Authorization: `Bearer ${token}` + } + } +) +``` + +## Example response +```200.json +{ + "result": "success", + "apikey": { + "key": "7ea12968-7758-40b6-8c73-75cc99be762b", + "level": 3, + "expiresAt": "2022-11-06T15:57:30.190Z", + "name": "My first API key", + "userId": 61 + } +} +``` diff --git a/markdown/dev/reference/backend/api/apikeys/whoami/en.md b/markdown/dev/reference/backend/api/apikeys/whoami/en.md new file mode 100644 index 00000000000..eda6f3634ef --- /dev/null +++ b/markdown/dev/reference/backend/api/apikeys/whoami/en.md @@ -0,0 +1,73 @@ +--- +title: Read the current API key +--- + +Reads the current API key used to authenticate the request. +For obvious reasons, this endpoint is only available with API key authentication. +However, there's an equivalent endpoint for JWT authentication. + +## Access control + +- [Permission level](/reference/backend/api/rbac) `0` or higher is required to read the current API key + +## Endpoints + +| Method | Path | Authentication | +| --------: | :--- | :------------- | +| | `/whoami/key` | [API Key & Secret](/reference/backend/api/authentication#key-authentication) | + +## Response status codes + +Possible status codes for these endpoints are: + +| Status code | Description | +| ----------: | :---------- | +| | success | +| | the request was malformed | +| | the request lacks authentication | +| | authentication failed | +| | server error | + + +If the status code is not the `error` property +in the response body should indicate the nature of the problem. + + +## Response body + +| Value | Type | Description | +| ------------------- | -------- | ----------- | +| `result` | `string` | `success` on success, and `error` on error | +| `error` | `string` | Will give info on the nature of the error. Only set if an error occured. | +| `apikey.key` | `string` | The API key | +| `apikey.level` | `number` | The privilege level of the API key | +| `apikey.expiresAt` | `string` | A string representation of the moment the API key expires | +| `apikey.name` | `string` | The name of the API key | +| `apikey.userId` | `number` | The ID of the user who created the API key | + +## Example request + +```js +const keyInfo = await axios.get( + 'https://backend.freesewing.org/whoami/key', + { + auth: { + username: apikey.key, + password: apikey.secret, + } + } +) +``` + +## Example response +```200.json +{ + "result": "success", + "apikey": { + "key": "7ea12968-7758-40b6-8c73-75cc99be762b", + "level": 3, + "expiresAt": "2022-11-06T15:57:30.190Z", + "name": "My first API key", + "userId": 61 + } +} diff --git a/markdown/dev/reference/backend/api/authentication/en.md b/markdown/dev/reference/backend/api/authentication/en.md new file mode 100644 index 00000000000..e8c0f9a0a66 --- /dev/null +++ b/markdown/dev/reference/backend/api/authentication/en.md @@ -0,0 +1,64 @@ +--- +title: Authentication +--- + +The FreeSewing backend API requires authentication for all but a handfull of +endpoints. + +The API supports two different types of authentication: + +| Type | Name | Description | +| ---- | ---- | ----------- | +| [JSON Web Tokens](#jwt-authentication) | `jwt` | This is typically used to authenticate humans in a browser session. | +| [API Keys](#key-authentication) | `key` | This is typically used to interact with the API in an automated way. Like in a script, a CI/CD context, a serverless runner, and so on. | + +While the API supports both, they are not supported on the same endpoint. +Instead, add the authentication type you want to use as the final part of +endpoint: + +- `/some/endpoint/jwt` : Authenticate with a JSON Web Token +- `/some/endpoint/key` : Authenticate with an API key and secret + +## `jwt` authentication + +The use of JSON Web Tokens ([jwt](https://jwt.io)) is typically used in a +browser context where we want to establish a *session*. + +To get a token, you must first authenticate at the [`/login`](/reference/backend/api/user/login) endpoint. +You will receive a JSON Web Token (jwt) as part of the response. + +In subsequent API calls, you must then include this token in the +`Authorization` header prefixed by `Bearer `. Like his: + +```js +const account = await axios.get( + `https://backend.freesewing.org/account/jwt`, + { + headers: { + Authorization: `Bearer ${token}` + } + } +) +``` + +## `key` authentication + +The combination of API key & secret serves as a username & password for [HTTP +basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication). + + +In basic authentication, the password is sent +unencrypted. To guard against this, this API should only be served over a +connection that is encrypted with TLS. (a url starting with `https://`). + + +Sending a username and password with a request like this is supported +pretty much everywhere. In addition, there is no need to establish a session +first, so this make the entire transation stateless. + +Below is an example using curl: + +```sh +curl -u api-key-here:api-secret-here \ + https://backend.freesewing.org/account/key +``` diff --git a/markdown/dev/reference/backend/api/en.md b/markdown/dev/reference/backend/api/en.md index a82a56d56aa..0139b7cd42b 100644 --- a/markdown/dev/reference/backend/api/en.md +++ b/markdown/dev/reference/backend/api/en.md @@ -1,6 +1,5 @@ --- -title: Backend REST API -linktitle: REST API +title: REST API --- This is the reference documentation for the FreeSewing backend REST API. diff --git a/markdown/dev/reference/backend/api/patterns/clone/en.md b/markdown/dev/reference/backend/api/patterns/clone/en.md new file mode 100644 index 00000000000..13c4707f438 --- /dev/null +++ b/markdown/dev/reference/backend/api/patterns/clone/en.md @@ -0,0 +1,110 @@ +--- +title: Clone a Pattern +--- + +Create a new Pattern by cloning an existing one. + +## Access control + +The [Permission level](/reference/backend/api/rbac) required to clone a +Pattern depends on: + +- Whether the Pattern is `public` +- Who created the Pattern + +The details are outlined in the table below: + +| | Public Patterns | Non-Public Patterns | +| ---------------: | :-------------: | :-----------------: | +| **Your own** | `0` or higher | `4` or higher | +| **Other user's** | `0` or higher | `5` or higher | + +## Endpoints + +Creating a new API key is possible via these endpoints: + +| Method | Path | Authentication | +| --------: | :--- | :------------- | +| | `/patterns/:id/clone/jwt` | [JSON Web Token](/reference/backend/api/authentication#jwt-authentication) | +| | `/patterns/:id/clone/key` | [API Key & Secret](/reference/backend/api/authentication#key-authentication) | + +## Request url + +The url should contain the ID of the Pattern you wish to remove. +It replaces the `:id` placeholder in the [endpoints listed above](#endpoints). + +## Response status codes + +Possible status codes for these endpoints are: + +| Status code | Description | +| ----------: | :---------- | +| | success | +| | the request was malformed | +| | the request lacks authentication | +| | authentication failed | +| | server error | + + +If the status code is not the `error` property +in the response body should indicate the nature of the problem. + + +## Response body + +| Value | Type | Description | +| ------------------- | -------- | ----------- | +| `result` | String | Either `success` or `error` | +| `error` | String | Will give info on the nature of the error. Only set if an error occured. | +| `pattern.id` | Number | The ID of the Pattern | +| `pattern.createdAt` | String | Date string indicating the moment the pattern was created | +| `pattern.data` | Object | Any additional data that was stored with Pattern data | +| `pattern.design` | String | The name of the design of which this Pattern is an instance | +| `pattern.img` | String | The URL to the image stored with this Pattern | +| `pattern.name` | String | The name of the Pattern | +| `pattern.notes` | String | The notes stored with the Pattern | +| `pattern.personId` | Number | The ID of the Person for whom the Pattern was created | +| `pattern.public` | Boolean| Indicates whether the Pattern is publicly accessible or not | +| `pattern.settings` | Object | The settings used to (re-)create the Pattern | +| `pattern.userId` | Number | The ID of the user who created the Pattern | +| `pattern.updatedAt` | String | Date string indicating the last time the pattern was updated | + +## Example request + +```js +const apiKey = axios.post( + 'https://backend.freesewing.org/patterns/10/clone/jwt', + null, + { + headers: { + Authorization: `Bearer ${token}` + } + } +) +``` + +## Example response +```200.json +{ + "result": "success", + "pattern": { + "id": 19, + "createdAt": "2022-11-19T16:29:33.346Z", + "data": { + "some": "value" + }, + "design": "aaron", + "img": "https://cdn.sanity.io/images/hl5bw8cj/production/a1565c8c6c70cfe7ea0fdf5c65501cd885adbe78-200x187.png", + "name": "Just a test", + "notes": "These are my notes", + "personId": 17, + "public": true, + "settings": { + "sa": 5 + }, + "userId": 10, + "updatedAt": "2022-11-19T16:29:33.346Z" + } +} +``` + diff --git a/markdown/dev/reference/backend/api/patterns/create/en.md b/markdown/dev/reference/backend/api/patterns/create/en.md new file mode 100644 index 00000000000..d38c6e29067 --- /dev/null +++ b/markdown/dev/reference/backend/api/patterns/create/en.md @@ -0,0 +1,121 @@ +--- +title: Create a Pattern +--- + +Creates a new Pattern. This is typically used when users choose to save a pattern. + +## Access control + +- [Permission level](/reference/backend/api/rbac) `4` or higher is required to create a Pattern + +## Endpoints + +Creating a new Pattern is possible via these endpoints: + +| Method | Path | Authentication | +| --------: | :--- | :------------- | +| | `/patterns/jwt` | [JSON Web Token](/reference/backend/api/authentication#jwt-authentication) | +| | `/patterns/key` | [API Key & Secret](/reference/backend/api/authentication#key-authentication) | + +## Request body +The request body is a JSON object with the following properties: + +| Property | Type | Description | +| ----------: | :------- | :---------- | +| `data` | `object` | Any additional data to store with the pattern | +| `design` | `string` | The name of the design this Pattern is an instance of | +| `img` | `object` | An image [data-uri][duri] to store with this Pattern | +| `name` | `string` | A name for the Pattern | +| `notes` | `string` | User notes for the pattern | +| `person` | `object` | The ID of the person to associate with this pattern | +| `settings` | `object` | The settings object to (re-)create the Pattern | + +## Response status codes + +Possible status codes for these endpoints are: + +| Status code | Description | +| ----------: | :---------- | +| | success | +| | the request was malformed | +| | the request lacks authentication | +| | authentication failed | +| | server error | + + +If the status code is not the `error` property +in the response body should indicate the nature of the problem. + + +## Response body + +| Value | Type | Description | +| ------------------- | -------- | ----------- | +| `result` | String | Either `success` or `error` | +| `error` | String | Will give info on the nature of the error. Only set if an error occured. | +| `pattern.id` | Number | The ID of the Pattern | +| `pattern.createdAt` | String | Date string indicating the moment the pattern was created | +| `pattern.data` | Object | Any additional data that was stored with Pattern data | +| `pattern.design` | String | The name of the design of which this Pattern is an instance | +| `pattern.img` | String | The URL to the image stored with this Pattern | +| `pattern.name` | String | The name of the Pattern | +| `pattern.notes` | String | The notes stored with the Pattern | +| `pattern.personId` | Number | The ID of the Person for whom the Pattern was created | +| `pattern.public` | Boolean| Indicates whether the Pattern is publicly accessible or not | +| `pattern.settings` | Object | The settings used to (re-)create the Pattern | +| `pattern.userId` | Number | The ID of the user who created the Pattern | +| `pattern.updatedAt` | String | Date string indicating the last time the pattern was updated | + +## Example request + +```js +const pattern = await axios.post( + 'https://backend.freesewing.org/patterns/jwt', + { + data: { + some: 'value', + } + design: "aaron", + img: "data:image/png;base64,iVBORw0KGgoAAAANSUhEU...truncated", + name: "Just a test", + notes: "These are my notes", + person: 17, + public: true, + settings: { + sa: 5, + }, + }, + { + headers: { + Authorization: `Bearer ${token}` + } + } +) +``` + +## Example response +```200.json +{ + "result": "success", + "pattern": { + "id": 10, + "createdAt": "2022-11-19T16:29:33.346Z", + "data": { + "some": "value" + }, + "design": "aaron", + "img": "https://cdn.sanity.io/images/hl5bw8cj/production/a1565c8c6c70cfe7ea0fdf5c65501cd885adbe78-200x187.png", + "name": "Just a test", + "notes": "These are my notes", + "personId": 17, + "public": true, + "settings": { + "sa": 5 + }, + "userId": 10, + "updatedAt": "2022-11-19T16:29:35.023Z" + } +} +``` + +[duri]: https://en.wikipedia.org/wiki/Data_URI_scheme diff --git a/markdown/dev/reference/backend/api/patterns/delete/en.md b/markdown/dev/reference/backend/api/patterns/delete/en.md new file mode 100644 index 00000000000..21478d69f80 --- /dev/null +++ b/markdown/dev/reference/backend/api/patterns/delete/en.md @@ -0,0 +1,59 @@ +--- +title: Delete a Pattern +--- + +Deletes an existing Pattern. + +## Access control + +- [Permission level](/reference/backend/api/rbac) `4` or higher is required to delete a Pattern +- [Permission level](/reference/backend/api/rbac) `8` is required to delete **another user's** Pattern + +## Endpoints + +Deleting a Pattern is possible via these endpoints: + +| Method | Path | Authentication | +| --------: | :--- | :------------- | +| | `/patterns/:id/jwt` | [JSON Web Token](/reference/backend/api/authentication#jwt-authentication) | +| | `/patterns/:id/key` | [API Key & Secret](/reference/backend/api/authentication#key-authentication) | + +## Request url + +The url should contain the ID of the Pattern you wish to remove. +It replaces the `:id` placeholder in the [endpoints listed above](#endpoints). + +## Response status codes + +Possible status codes for these endpoints are: + +| Status code | Description | +| ----------: | :---------- | +| | success | +| | the request was malformed | +| | the request lacks authentication | +| | authentication failed | +| | server error | + +## Example request + +```js +await axios.delete( + 'https://backend.freesewing.org/patterns/10/jwt', + { + headers: { + Authorization: `Bearer ${token}` + } + } +) +``` + +## Example response + +```204.json +``` + +These endpoints return status code (no content) on +success, with no response body. + + diff --git a/markdown/dev/reference/backend/api/patterns/en.md b/markdown/dev/reference/backend/api/patterns/en.md new file mode 100644 index 00000000000..1f3060b74be --- /dev/null +++ b/markdown/dev/reference/backend/api/patterns/en.md @@ -0,0 +1,34 @@ +--- +title: Patterns +--- + +Patterns hold information on and settings for a user's patterns. + +## Endpoints + + + +## Notes + +### The `design` property should hold the design + +The `design` property should hold the name of the FreeSewing design. +For example, `aaron`. + +### The `notes` vs `data` properties + +Both the `data` and `notes` properties can hold additional information about +the pattern. + +Keep in mind that: +- The `notes` property is intended to be used by the user to add notes about + their pattern. It will only accept data of type `string`. +- The `data` property is intended to allow frontend developers to store + additional data about the pattern. It wil only accept data of type `object`. + +### The `settings` property should hold the pattern settings + +The `settings` property should hold [a settings object](/reference/settings) +that can be passed to [the Pattern +constructor](/reference/api/pattern#creating-a-pattern). + diff --git a/markdown/dev/reference/backend/api/patterns/read/en.md b/markdown/dev/reference/backend/api/patterns/read/en.md new file mode 100644 index 00000000000..8ac0aeb6820 --- /dev/null +++ b/markdown/dev/reference/backend/api/patterns/read/en.md @@ -0,0 +1,109 @@ +--- +title: Read a Pattern +--- + +Reads an existing Pattern. + +## Access control + +The [Permission level](/reference/backend/api/rbac) required to read a +Pattern depends on: + +- Whether the Pattern is `public` +- Who created the Pattern + +The details are outlined in the table below: + +| | Public Patterns | Non-Public Patterns | +| ---------------: | :-------------: | :-----------------: | +| **Your own** | `0` or higher | `4` or higher | +| **Other user's** | `0` or higher | `5` or higher | + +## Endpoints + +Reading a Pattern is possible via these endpoints: + +| Method | Path | Authentication | +| --------: | :--- | :------------- | +| | `/patterns/:id/jwt` | [JSON Web Token](/reference/backend/api/authentication#jwt-authentication) | +| | `/patterns/:id/key` | [API Key & Secret](/reference/backend/api/authentication#key-authentication) | + +## Request url + +The url should contain the ID of the Pattern you wish to read. +It replaces the `:id` placeholder in the [endpoints listed above](#endpoints). + +## Response status codes + +Possible status codes for these endpoints are: + +| Status code | Description | +| ----------: | :---------- | +| | success | +| | the request was malformed | +| | the request lacks authentication | +| | authentication failed | +| | API key not found | +| | server error | + + +If the status code is not the `error` property +in the response body should indicate the nature of the problem. + + +## Response body + +| Value | Type | Description | +| ------------------- | -------- | ----------- | +| `result` | String | Either `success` or `error` | +| `error` | String | Will give info on the nature of the error. Only set if an error occured. | +| `pattern.id` | Number | The ID of the Pattern | +| `pattern.createdAt` | String | Date string indicating the moment the pattern was created | +| `pattern.data` | Object | Any additional data that was stored with Pattern data | +| `pattern.design` | String | The name of the design of which this Pattern is an instance | +| `pattern.img` | String | The URL to the image stored with this Pattern | +| `pattern.name` | String | The name of the Pattern | +| `pattern.notes` | String | The notes stored with the Pattern | +| `pattern.personId` | Number | The ID of the Person for whom the Pattern was created | +| `pattern.public` | Boolean| Indicates whether the Pattern is publicly accessible or not | +| `pattern.settings` | Object | The settings used to (re-)create the Pattern | +| `pattern.userId` | Number | The ID of the user who created the Pattern | +| `pattern.updatedAt` | String | Date string indicating the last time the pattern was updated | + +## Example request + +```js +const pattern = await axios.get( + 'https://backend.freesewing.org/patterns/10/jwt', + { + headers: { + Authorization: `Bearer ${token}` + } + } +) +``` + +## Example response +```200.json +{ + "result": "success", + "pattern": { + "id": 10, + "createdAt": "2022-11-19T16:29:33.346Z", + "data": { + "some": "value" + }, + "design": "aaron", + "img": "https://cdn.sanity.io/images/hl5bw8cj/production/a1565c8c6c70cfe7ea0fdf5c65501cd885adbe78-200x187.png", + "name": "Just a test", + "notes": "These are my notes", + "personId": 17, + "public": true, + "settings": { + "sa": 5 + }, + "userId": 10, + "updatedAt": "2022-11-19T16:29:35.023Z" + } +} +``` diff --git a/markdown/dev/reference/backend/api/patterns/update/en.md b/markdown/dev/reference/backend/api/patterns/update/en.md new file mode 100644 index 00000000000..75808ffe6b2 --- /dev/null +++ b/markdown/dev/reference/backend/api/patterns/update/en.md @@ -0,0 +1,119 @@ +--- +title: Update a Pattern +--- + +Updates an existing Pattern. +This is typically used when users choose to save/overwrite a pattern. + +## Access control + +- [Permission level](/reference/backend/api/rbac) `4` or higher is required to update a Pattern +- [Permission level](/reference/backend/api/rbac) `8` is required to update **another user's** Pattern + +## Endpoints + +Updating an existing Pattern is possible via these endpoints: + +| Method | Path | Authentication | +| --------: | :--- | :------------- | +| | `/patterns/:id/jwt` | [JSON Web Token](/reference/backend/api/authentication#jwt-authentication) | +| | `/patterns/:id/key` | [API Key & Secret](/reference/backend/api/authentication#key-authentication) | + +## Request url + +The url should contain the ID of the Pattern you wish to remove. +It replaces the `:id` placeholder in the [endpoints listed above](#endpoints). + +## Request body + +| Property | Type | Description | +| ----------: | :------- | :---------- | +| `data` | `object` | Any additional data to store with the pattern | +| `design` | `string` | The name of the design this Pattern is an instance of | +| `img` | `object` | An image [data-uri][duri] to store with this Pattern | +| `name` | `string` | A name for the Pattern | +| `notes` | `string` | User notes for the pattern | +| `person` | `object` | The ID of the person to associate with this pattern | +| `settings` | `object` | The settings object to (re-)create the Pattern | + +## Response status codes + +Possible status codes for these endpoints are: + +| Status code | Description | +| ----------: | :---------- | +| | success | +| | the request was malformed | +| | the request lacks authentication | +| | authentication failed | +| | server error | + + +If the status code is not the `error` property +in the response body should indicate the nature of the problem. + + +## Response body + +| Value | Type | Description | +| ------------------- | -------- | ----------- | +| `result` | String | Either `success` or `error` | +| `error` | String | Will give info on the nature of the error. Only set if an error occured. | +| `pattern.id` | Number | The ID of the Pattern | +| `pattern.createdAt` | String | Date string indicating the moment the pattern was created | +| `pattern.data` | Object | Any additional data that was stored with Pattern data | +| `pattern.design` | String | The name of the design of which this Pattern is an instance | +| `pattern.img` | String | The URL to the image stored with this Pattern | +| `pattern.name` | String | The name of the Pattern | +| `pattern.notes` | String | The notes stored with the Pattern | +| `pattern.personId` | Number | The ID of the Person for whom the Pattern was created | +| `pattern.public` | Boolean| Indicates whether the Pattern is publicly accessible or not | +| `pattern.settings` | Object | The settings used to (re-)create the Pattern | +| `pattern.userId` | Number | The ID of the user who created the Pattern | +| `pattern.updatedAt` | String | Date string indicating the last time the pattern was updated | + +## Example request + +```js +const udpate = await axios.put( + 'https://backend.freesewing.org/patterns/10/jwt', + { + data: { + some: 'new value', + } + public: false, + }, + { + headers: { + Authorization: `Bearer ${token}` + } + } +) +``` + +## Example response +```200.json +{ + "result": "success", + "pattern": { + "id": 10, + "createdAt": "2022-11-19T16:29:33.346Z", + "data": { + "some": "new value" + }, + "design": "aaron", + "img": "https://cdn.sanity.io/images/hl5bw8cj/production/a1565c8c6c70cfe7ea0fdf5c65501cd885adbe78-200x187.png", + "name": "Just a test", + "notes": "These are my notes", + "personId": 17, + "public": false, + "settings": { + "sa": 5 + }, + "userId": 10, + "updatedAt": "2022-11-19T16:43:39.223Z" + } +} +``` + +[duri]: https://en.wikipedia.org/wiki/Data_URI_scheme diff --git a/markdown/dev/reference/backend/api/rbac/en.md b/markdown/dev/reference/backend/api/rbac/en.md new file mode 100644 index 00000000000..c43c2796c60 --- /dev/null +++ b/markdown/dev/reference/backend/api/rbac/en.md @@ -0,0 +1,42 @@ +--- +title: Access control +--- + +The backend API implements role-based access control (RBAC). Each +user has a role and that role determines what they can and cannot do. + +## Roles vs levels + +In practice, the different user roles map to a permission level between +`0` and `8`. +The available roles and their privilege levels are: + +- **user**: `4` +- **bughunter**: `5` +- **support**: `6` +- **admin**: `8` + +We offer more fine-grained control over the permission level when +authenticating with API keys. When you create an API key, you can choose any +permissing level that is equal or lower than your own role's permission level. + +This allows you to -- for example -- generate an API key that only have read +access to your data. + +## Permission levels + +The table below lists the priviledge of all levels as well as their +corresponding `role` + +| Level | Abilities | `user` | `bughunter` | `support` | `admin` | +| --: | -- | :--: | :--: | :--: | :--: | +| `0` | authenticate | ✅ | ✅ | ✅ | ✅ | +| `1` | **read** measurements and patterns | ✅ | ✅ | ✅ | ✅ | +| `2` | **read all** account data | ✅ | ✅ | ✅ | ✅ | +| `3` | **write** measurements or patterns | ✅ | ✅ | ✅ | ✅ | +| `4` | **write all** account data | ✅ | ✅ | ✅ | ✅ | +| `5` | **read** measurements or patterns of **other users** | ❌ | ✅ | ✅ | ✅ | +| `6` | **read all** account data of **other users** | ❌ | ❌ | ✅ | ✅ | +| `7` | **write** account data of **other users** through **specific support methods** | ❌ | ❌ | ✅ | ✅ | +| `8` | impersonate other users, **full write access** | ❌ | ❌ | ❌ | ✅ | + diff --git a/markdown/dev/reference/backend/db/en.md b/markdown/dev/reference/backend/db/en.md new file mode 100644 index 00000000000..e10828f0ff7 --- /dev/null +++ b/markdown/dev/reference/backend/db/en.md @@ -0,0 +1,167 @@ +--- +title: Database schema +--- + +The database schema for the backend is available in the [prisma.schema][prisma] +file in our monorepo. For your convenience, we're replicated the info below. + + +In the tables below, the following symbols are used: + +- 🔑 : Field holds the primary key +- 🗝️ : Field holds a foreign key +- ❄️ : Field must be unique +- ⚡ : Field is indexed +- 🔐 : Field is encrypted +- 🧂 : Field is salted & hashed + + + +## Table Apikey + +| | Field | Type | Default | +| -: | :---------- | :------- | :------ | +| | `createdAt` | DateTime | `now()` | +| | `expiresAt` | DateTime | | +| 🔑 | `id` | String (UUID) | `uuid()` | +| | `level` | Int | `0` | +| | `name` | String | | +| 🧂 | `secret` | String(ified JSON) | | +| 🗝️ | `userId` | Int | | + +```json +{ + "createdAt": 1668448465659, + "expiresAt": 1668448525632, + "id": "5122738c-2e44-4165-a9db-2f0b6ba4fbaf", + "level": 4, + "name": "Test API key", + "secret": "{\"type\":\"v3\",\"hash\":\"a88747549512991836332813ccc82abba652bf5835dfa0a5a8177e2ddacf1f2d0f6340a6c36c0e816cf7a9976847bb14010c658703d4fa1bc4a0f3bdf3f187c2\",\"salt\":\"9eba4d132010a534a54d76fa9a6083eb\"}", + "userId": 10 +} +``` + +## Table Confirmation + +| | Field | Type | Default | +| -: | :---------- | :------- | :------ | +| | `createdAt` | DateTime | `now()` | +| 🔐 | `data` | String(ified JSON) | | +| 🔑 | `id` | String (UUID) | `uuid()` | +| | `type` | String | | +| 🗝️ | `userId` | Int | | + +```json +{ + "createdAt": 1668447926731, + "data": "{\"iv\":\"01a57e75817327cc99b08c5fabdf4c89\",\"ct\":\"03b89a100a67dd85898a313fd832c720700f4b3a800aa715a6bbbe66a4f2ab01f02215e995b0983b609ef8606912546fb27b63ea5436c5bbacd73cb9363024e8339d5f25899c3a7172b66991aed3e9a6c8dda850bea0d40c84f630f0f9f76b80052810639ee6f62a97ac9e45cab3c05abd768ff6ca22b8b4255993dc76023c948f80ee208e6455c28097402f63fe26afeab8d082e12bef760320ce3a5d2ea123\"}", + "id": "2319e09e-ffe0-4781-9add-46d73a57bb34", + "type": "signup", + "userId": 9 +} +``` + +## Table Subscriber + +| | Field | Type | Default | +| -: | :---------- | :------- | :------ | +| | `createdAt` | DateTime | `now()` | +| 🔐 | `data` | String(ified JSON) | | +| ❄️ | `ehash` | String | | +| 🔐 | `email` | String(ified JSON) | | +| 🔑 | `id` | String (UUID) | `uuid()` | +| | `updatedAt` | DateTime | | + +Include example + +## Table User + +| | Field | Type | Default | +| -: | :---------- | :------- | :------ | +| 🔐 | `bio` | String(ified JSON) | | +| | `consent` | Int | | +| | `control` | Int | | +| | `createdAt` | DateTime | `now()` | +| ❄️ | `ehash` | String | | +| 🔐 | `email` | String(ified JSON) | | +| 🔐 | `github` | String(ified JSON) | | +| 🔑 | `id` | Int | | +| ⚡ | `ihash` | String | | +| 🔐 | `img` | String(ified JSON) | | +| | `imperial` | Boolean | `false` | +| 🔐 | `initial` | String(ified JSON) | | +| | `language` | String | `en` | +| | `lastLogin` | DateTime | | +| ❄️ | `lusername` | String | | +| | `newsletter`| Boolean | `false` | +| 🧂 | `password` | String(ified JSON) | | +| | `patron` | Int | `0` | +| | `role` | String | `user` | +| | `status` | Int | `0` | +| | `updatedAt` | DateTime | | +| | `username` | String | | + +```json +{ + "bio": "{\"iv\":\"92bc05493831ccd92c522a7c1395058d\",\"ct\":\"5a520bf84a1e194c87e294f1e28c6100\"}", + "consent": 1, + "control": 1, + "createdAt": 1668447881259, + "ehash": "57c20ec1135355d378145a994bc26ce1f1720e382474116cafbd5f4c3fb676c3", + "email": "{\"iv\":\"b48d8e4769e359cec69f53629c5ad4ce\",\"ct\":\"57211cdeb0c1d5641aae953b10058e8ad94604df8183a3f5c99517620858674696e1e988ccf8e527f132095c2f303a12\"}", + "github": "{\"iv\":\"42ab19245da9a4d35d5b5ea66c5897a9\",\"ct\":\"7b3b39bf1811e46e887d81222e99251d\"}", + "id": 7, + "ihash": "57c20ec1135355d378145a994bc26ce1f1720e382474116cafbd5f4c3fb676c3", + "img": "{\"iv\":\"2757b3674a4adb71a535d5d37e923469\",\"ct\":\"e80750ff6bfa7f8c1694558a73673c8714948d67425c5cf01848be8decd612ef8d3129b34c4140f665a5ad7919f32616\"}", + "imperial": 0, + "initial": "{\"iv\":\"b48d8e4769e359cec69f53629c5ad4ce\",\"ct\":\"57211cdeb0c1d5641aae953b10058e8ad94604df8183a3f5c99517620858674696e1e988ccf8e527f132095c2f303a12\"}", + "language": "en", + "lastLogin": 1668447881273, + "lusername": "user-7", + "newsletter": 0, + "password": "{\"type\":\"v3\",\"hash\":\"bc5b64b1932a5d4a053777011a61a244e07edfe78eaa52beb56d08bf0ec3f8d8946e8749c872db5a03d5720e53b8da1d6df3ee12d96443d107c01dd2392e2848\",\"salt\":\"648d1218a93ded40ad42cc5bed603bcf\"}", + "patron": 0, + "role": "user", + "status": 1, + "updatedAt": 1668447881274, + "username": "user-7" +} +``` + +## Table Pattern + +| | Field | Type | Default | +| -: | :---------- | :------- | :------ | +| 🔑 | `id` | Int | | +| | `createdAt` | DateTime | `now()` | +| 🔐 | `data` | String(ified JSON) | | +| | `design` | String | | +| 🔐 | `img` | String(ified JSON) | | +| 🔐 | `name` | String(ified JSON) | | +| 🔐 | `notes` | String(ified JSON) | | +| 🗝️ | `personId` | Int | | +| | `public` | Boolean | `false` | +| 🔐 | `settings` | String(ified JSON) | | +| 🗝️ | `userId` | Int | | +| | `updatedAt` | DateTime | | + +Include example + +## Table Person + +| | Field | Type | Default | +| -: | :---------- | :------- | :------ | +| 🔑 | `id` | Int | | +| | `createdAt` | DateTime | `now()` | +| 🔐 | `img` | String(ified JSON) | | +| | `imperial` | Boolean | `false` | +| 🔐 | `measies` | String(ified JSON) | | +| 🔐 | `name` | String(ified JSON) | | +| 🔐 | `notes` | String(ified JSON) | | +| 🗝️ | `userId` | Int | | +| | `public` | Boolean | `false` | +| | `updatedAt` | DateTime | | + +Include example + +[prisma]: https://github.com/freesewing/freesewing/blob/develop/sites/backend/prisma/schema.prisma diff --git a/markdown/dev/reference/backend/en.md b/markdown/dev/reference/backend/en.md index fa686099df5..6a272cd44c4 100644 --- a/markdown/dev/reference/backend/en.md +++ b/markdown/dev/reference/backend/en.md @@ -1,5 +1,5 @@ --- -title: FreeSewing backend +title: FreeSewing Backend --- The FreeSewing backend handles all user data. Prior to version 3 of FreeSewing, @@ -14,37 +14,14 @@ environments and so on. In other words, we no longer merely provide our own frontend, you can now also use our backend as a service to build your own projects. -## Changes for developers +## Under the hood -We've made a number of changes to make it easier for external developers and -contributors to work with our backend. - -### Authentication with JWT and API keys - -Before version 3, the backend only supported authentication via JSON Web -Tokens. That's fine for a browser session, but not very handy if you want to -talk to the API directly. - -Since version 3, we support authentication with API keys. Furthermore, we -allow any FreeSewing user to generate their own API keys. - -In other words, if you want to connect to our backend API, you don't need to -ask us. You can generate your own API key and start right away. - -### Sqlite instead of MongoDB - -Our backend used to use MongoDB for storage. Since version 3, we've moved to -Sqlite which is a file-based database making local development a breeze since -you don't need to run a local database server. - -### Sanity instead of local storage - -We now use Sanity and the Sanity API to stored images for users (avatars for -user accounts and people). Furthermore, we also generate PDFs in the browser -now so we also don't need storage for that. - -As a result, our backend does not need any storage, only access to the Sqlite -file. This also makes it easier to work with the backend as a developer. +The FreeSewing backend is written in [NodeJS](https://nodejs.org/en/) on top of +[Express](https://expressjs.com/). It uses [Prisma](https://www.prisma.io/) to +interface with a [Sqlite database](https://www.sqlite.org/) database, +[Sanity](https://www.sanity.io/) to store images, [AWS SES]( +https://aws.amazon.com/ses/) to send out emails, and +[pino](https://github.com/pinojs/pino) for logging. ## Use, don't abuse