From 48e7eb10f1bc06fb07b8b14c91b036a12fbe367d Mon Sep 17 00:00:00 2001 From: Shyam Sunder Date: Mon, 5 Aug 2019 19:15:37 -0400 Subject: doc: moved documentation to a seperate folder --- doc/API.md | 2437 ++++++++++++++++++++++++++++++++++++++++++++ doc/INSTALL.md | 115 +++ doc/LEGACY_INSTALL.md | 237 +++++ doc/example.env | 18 + doc/nginx.vhost.production | 49 + 5 files changed, 2856 insertions(+) create mode 100644 doc/API.md create mode 100644 doc/INSTALL.md create mode 100644 doc/LEGACY_INSTALL.md create mode 100644 doc/example.env create mode 100644 doc/nginx.vhost.production (limited to 'doc') diff --git a/doc/API.md b/doc/API.md new file mode 100644 index 0000000..c23f045 --- /dev/null +++ b/doc/API.md @@ -0,0 +1,2437 @@ +`szurubooru` uses REST API for all operations. + + + +# Table of contents + +1. [General rules](#general-rules) + + - [Authentication](#authentication) + - [User token authentication](#user-token-authentication) + - [Basic requests](#basic-requests) + - [File uploads](#file-uploads) + - [Error handling](#error-handling) + - [Field selecting](#field-selecting) + - [Versioning](#versioning) + +2. [API reference](#api-reference) + + - Tag categories + - [Listing tag categories](#listing-tag-categories) + - [Creating tag category](#creating-tag-category) + - [Updating tag category](#updating-tag-category) + - [Getting tag category](#getting-tag-category) + - [Deleting tag category](#deleting-tag-category) + - [Setting default tag category](#setting-default-tag-category) + - Tags + - [Listing tags](#listing-tags) + - [Creating tag](#creating-tag) + - [Updating tag](#updating-tag) + - [Getting tag](#getting-tag) + - [Deleting tag](#deleting-tag) + - [Merging tags](#merging-tags) + - [Listing tag siblings](#listing-tag-siblings) + - Posts + - [Listing posts](#listing-posts) + - [Creating post](#creating-post) + - [Updating post](#updating-post) + - [Getting post](#getting-post) + - [Deleting post](#deleting-post) + - [Merging posts](#merging-posts) + - [Rating post](#rating-post) + - [Adding post to favorites](#adding-post-to-favorites) + - [Removing post from favorites](#removing-post-from-favorites) + - [Getting featured post](#getting-featured-post) + - [Featuring post](#featuring-post) + - [Reverse image search](#reverse-image-search) + - Comments + - [Listing comments](#listing-comments) + - [Creating comment](#creating-comment) + - [Updating comment](#updating-comment) + - [Getting comment](#getting-comment) + - [Deleting comment](#deleting-comment) + - [Rating comment](#rating-comment) + - Users + - [Listing users](#listing-users) + - [Creating user](#creating-user) + - [Updating user](#updating-user) + - [Getting user](#getting-user) + - [Deleting user](#deleting-user) + - User Tokens + - [Listing user tokens](#listing-user-tokens) + - [Creating user token](#creating-user-token) + - [Updating user token](#updating-user-token) + - [Deleting user token](#deleting-user-token) + - Password reset + - [Password reset - step 1: mail request](#password-reset---step-2-confirmation) + - [Password reset - step 2: confirmation](#password-reset---step-2-confirmation) + - Snapshots + - [Listing snapshots](#listing-snapshots) + - Global info + - [Getting global info](#getting-global-info) + - File uploads + - [Uploading temporary file](#uploading-temporary-file) + +3. [Resources](#resources) + + - [User](#user) + - [Micro user](#micro-user) + - [User token](#user-token) + - [Tag category](#tag-category) + - [Tag](#tag) + - [Micro tag](#micro-tag) + - [Post](#post) + - [Micro post](#micro-post) + - [Note](#note) + - [Comment](#comment) + - [Snapshot](#snapshot) + - [Unpaged search result](#unpaged-search-result) + - [Paged search result](#paged-search-result) + - [Image search result](#image-search-result) + +4. [Search](#search) + + + +# General rules + +## Authentication + +Authentication is achieved by means of [basic HTTP +auth](https://en.wikipedia.org/wiki/Basic_access_authentication) or through the +use of [user token authentication](#user-token-authentication). For this +reason, it is recommended to connect through HTTPS. There are no sessions, so +every privileged request must be authenticated. Available privileges depend on +the user's rank. The way how rank translates to privileges is defined in the +server's configuration. + +It is recommended to add `?bump-login` GET parameter to the first request in a +client "session" (where the definition of a session is up to the client), so +that the user's last login time is kept up to date. + +## User token authentication + +User token authentication works similarly to [basic HTTP +auth](https://en.wikipedia.org/wiki/Basic_access_authentication). Because it +operates similarly to ***basic HTTP auth*** it is still recommended to connect +through HTTPS. The authorization header uses the type of `Token` and the +username and token are encoded as Base64 and sent as the second parameter. + +Example header for user1:token-is-more-secure +``` +Authorization: Token dXNlcjE6dG9rZW4taXMtbW9yZS1zZWN1cmU= +``` + +The benefit of token authentication is that beyond the initial login to acquire +the first token, there is no need to transmit the user password in plaintext +via basic auth. Additionally tokens can be revoked at anytime allowing a +cleaner interface for isolating clients from user credentials. + +## Basic requests + +Every request must use `Content-Type: application/json` and `Accept: +application/json`. An exception to this rule are requests that upload files. + +## File uploads + +Requests that upload files must use `multipart/form-data` encoding. Any request +that bundles user files, must send the request data (which is JSON) as an +additional file with the special name of `metadata` (whereas the actual files +must have names specific to the API that is being used.) + +Alternatively, the server can download the files from the Internet on client's +behalf. In that case, the request doesn't need to be specially encoded in any +way. The files, however, should be passed as regular fields appended with a +`Url` suffix. For example, to use `http://example.com/file.jpg` in an API that +accepts a file named `content`, the client should pass +`{"contentUrl":"http://example.com/file.jpg"}` as a part of the JSON message +body. + +Finally, in some cases the user might want to reuse one file between the +requests to save the bandwidth (for example, reverse search + consecutive +upload). In this case one should use [temporary file +uploads](#uploading-temporary-file), and pass the tokens returned by the API as +regular fields appended with a `Token` suffix. For example, to use previously +uploaded data, which was given token `deadbeef`, in an API that accepts a file +named `content`, the client should pass `{"contentToken":"deadbeef"}` as part +of the JSON message body. If the file with the particular token doesn't exist +or it has expired, the server will show an error. + +## Error handling + +All errors (except for unhandled fatal server errors) send relevant HTTP status +code together with JSON of following structure: + +```json5 +{ + "name": "Name of the error, e.g. 'PostNotFoundError'", + "title": "Generic title of error message, e.g. 'Not found'", + "description": "Detailed description of what went wrong, e.g. 'User `rr-` not found." +} +``` + +List of possible error names: + +- `MissingRequiredFileError` +- `MissingRequiredParameterError` +- `InvalidParameterError` (when trying to pass text when integer is expected etc.) +- `IntegrityError` (race conditions when editing the same resource) +- `SearchError` +- `AuthError` +- `PostNotFoundError` +- `PostAlreadyFeaturedError` +- `PostAlreadyUploadedError` +- `InvalidPostIdError` +- `InvalidPostSafetyError` +- `InvalidPostSourceError` +- `InvalidPostContentError` +- `InvalidPostRelationError` +- `InvalidPostNoteError` +- `InvalidPostFlagError` +- `InvalidFavoriteTargetError` +- `InvalidCommentIdError` +- `CommentNotFoundError` +- `EmptyCommentTextError` +- `InvalidScoreTargetError` +- `InvalidScoreValueError` +- `TagCategoryNotFoundError` +- `TagCategoryAlreadyExistsError` +- `TagCategoryIsInUseError` +- `InvalidTagCategoryNameError` +- `InvalidTagCategoryColorError` +- `TagNotFoundError` +- `TagAlreadyExistsError` +- `TagIsInUseError` +- `InvalidTagNameError` +- `InvalidTagRelationError` +- `InvalidTagCategoryError` +- `InvalidTagDescriptionError` +- `UserNotFoundError` +- `UserAlreadyExistsError` +- `InvalidUserNameError` +- `InvalidEmailError` +- `InvalidPasswordError` +- `InvalidRankError` +- `InvalidAvatarError` +- `ProcessingError` (failed to generate thumbnail or download remote file) +- `ValidationError` (catch all for odd validation errors) + + +## Field selecting + +For performance considerations, sometimes the client might want to choose the +fields the server sends to it in order to improve the query speed. This +customization is available for top-level fields of most of the +[resources](#resources). To choose the fields, the client should pass +`?fields=field1,field2,...` suffix to the query. This works regardless of the +request type (`GET`, `PUT` etc.). + +For example, to list posts while getting only their IDs and tags, the client +should send a `GET` query like this: + +``` +GET /posts/?fields=id,tags +``` + +## Versioning + +To prevent problems with concurrent resource modification, szurubooru +implements optimistic locks using resource versions. Each modifiable resource +has its `version` returned to the client with `GET` requests. At the same time, +each `PUT` and `DELETE` request sent by the client must present the same +`version` field to the server with value as it was given in `GET`. + +For example, given `GET /post/1`, the server responds like this: + +``` +{ + ..., + "version": 2 +} +``` + +This means the client must then send `{"version": 2}` back too. If the client +fails to do so, the server will reject the request notifying about missing +parameter. If someone has edited the post in the mean time, the server will +reject the request as well, in which case the client is encouraged to notify +the user about the situation. + + +# API reference + +Depending on the deployment, the URLs might be relative to some base path such +as `/api/`. Values denoted with diamond braces (``) signify variable +data. + +## Listing tag categories +- **Request** + + `GET /tag-categories` + +- **Output** + + An [unpaged search result](#unpaged-search-result), for which `` + is a [tag category resource](#tag-category). + +- **Errors** + + - privileges are too low + +- **Description** + + Lists all tag categories. Doesn't use paging. + +## Creating tag category +- **Request** + + `POST /tag-categories` + +- **Input** + + ```json5 + { + "name": , + "color": + } + ``` + +- **Output** + + A [tag category resource](#tag-category). + +- **Errors** + + - the name is used by an existing tag category (names are case insensitive) + - the name is invalid or missing + - the color is invalid or missing + - privileges are too low + +- **Description** + + Creates a new tag category using specified parameters. Name must match + `tag_category_name_regex` from server's configuration. First category + created becomes the default category. + +## Updating tag category +- **Request** + + `PUT /tag-category/` + +- **Input** + + ```json5 + { + "version": , + "name": , // optional + "color": , // optional + } + ``` + +- **Output** + + A [tag category resource](#tag-category). + +- **Errors** + + - the version is outdated + - the tag category does not exist + - the name is used by an existing tag category (names are case insensitive) + - the name is invalid + - the color is invalid + - privileges are too low + +- **Description** + + Updates an existing tag category using specified parameters. Name must + match `tag_category_name_regex` from server's configuration. All fields + except the [`version`](#versioning) are optional - update concerns only + provided fields. + +## Getting tag category +- **Request** + + `GET /tag-category/` + +- **Output** + + A [tag category resource](#tag-category). + +- **Errors** + + - the tag category does not exist + - privileges are too low + +- **Description** + + Retrieves information about an existing tag category. + +## Deleting tag category +- **Request** + + `DELETE /tag-category/` + +- **Input** + + ```json5 + { + "version": + } + ``` + +- **Output** + + ```json5 + {} + ``` + +- **Errors** + + - the version is outdated + - the tag category does not exist + - the tag category is used by some tags + - the tag category is the last tag category available + - privileges are too low + +- **Description** + + Deletes existing tag category. The tag category to be deleted must have no + usages. + +## Setting default tag category +- **Request** + + `PUT /tag-category//default` + +- **Input** + + ```json5 + {} + ``` + +- **Output** + + A [tag category resource](#tag-category). + +- **Errors** + + - the tag category does not exist + - privileges are too low + +- **Description** + + Sets given tag category as default. All new tags created manually or + automatically will have this category. + +## Listing tags +- **Request** + + `GET /tags/?offset=&limit=&query=` + +- **Output** + + A [paged search result resource](#paged-search-result), for which + `` is a [tag resource](#tag). + +- **Errors** + + - privileges are too low + +- **Description** + + Searches for tags. + + **Anonymous tokens** + + Same as `name` token. + + **Named tokens** + + | `` | Description | + | ------------------- | ----------------------------------------- | + | `name` | having given name (accepts wildcards) | + | `category` | having given category (accepts wildcards) | + | `creation-date` | created at given date | + | `creation-time` | alias of `creation-date` | + | `last-edit-date` | edited at given date | + | `last-edit-time` | alias of `last-edit-date` | + | `edit-date` | alias of `last-edit-date` | + | `edit-time` | alias of `last-edit-date` | + | `usages` | used in given number of posts | + | `usage-count` | alias of `usages` | + | `post-count` | alias of `usages` | + | `suggestion-count` | with given number of suggestions | + | `implication-count` | with given number of implications | + + **Sort style tokens** + + | `` | Description | + | ------------------- | ---------------------------- | + | `random` | as random as it can get | + | `name` | A to Z | + | `category` | category (A to Z) | + | `creation-date` | recently created first | + | `creation-time` | alias of `creation-date` | + | `last-edit-date` | recently edited first | + | `last-edit-time` | alias of `creation-time` | + | `edit-date` | alias of `creation-time` | + | `edit-time` | alias of `creation-time` | + | `usages` | used in most posts first | + | `usage-count` | alias of `usages` | + | `post-count` | alias of `usages` | + | `suggestion-count` | with most suggestions first | + | `implication-count` | with most implications first | + + **Special tokens** + + None. + +## Creating tag +- **Request** + + `POST /tags` + +- **Input** + + ```json5 + { + "names": [, , ...], + "category": , + "description": , // optional + "implications": [, , ...], // optional + "suggestions": [, , ...] // optional + } + ``` + +- **Output** + + A [tag resource](#tag). + +- **Errors** + + - any name is used by an existing tag (names are case insensitive) + - any name, implication or is invalid + - category is invalid + - no name was specified + - implications or suggestions contain any item from names (e.g. there's a + shallow cyclic dependency) + - privileges are too low + +- **Description** + + Creates a new tag using specified parameters. Names, suggestions and + implications must match `tag_name_regex` from server's configuration. + Category must exist and is the same as `name` field within + [`` resource](#tag-category). Suggestions and implications + are optional. If specified implied tags or suggested tags do not exist yet, + they will be automatically created. Tags created automatically have no + implications, no suggestions, one name and their category is set to the + first tag category found. If there are no tag categories established yet, + an error will be thrown. + +## Updating tag +- **Request** + + `PUT /tag/` + +- **Input** + + ```json5 + { + "version": , + "names": [, , ...], // optional + "category": , // optional + "description": , // optional + "implications": [, , ...], // optional + "suggestions": [, , ...] // optional + } + ``` + +- **Output** + + A [tag resource](#tag). + +- **Errors** + + - the version is outdated + - the tag does not exist + - any name is used by an existing tag (names are case insensitive) + - any name, implication or suggestion name is invalid + - category is invalid + - implications or suggestions contain any item from names (e.g. there's a + shallow cyclic dependency) + - privileges are too low + +- **Description** + + Updates an existing tag using specified parameters. Names, suggestions and + implications must match `tag_name_regex` from server's configuration. + Category must exist and is the same as `name` field within + [`` resource](#tag-category). If specified implied tags or + suggested tags do not exist yet, they will be automatically created. Tags + created automatically have no implications, no suggestions, one name and + their category is set to the first tag category found. All fields except + the [`version`](#versioning) are optional - update concerns only provided + fields. + +## Getting tag +- **Request** + + `GET /tag/` + +- **Output** + + A [tag resource](#tag). + +- **Errors** + + - the tag does not exist + - privileges are too low + +- **Description** + + Retrieves information about an existing tag. + +## Deleting tag +- **Request** + + `DELETE /tag/` + +- **Input** + + ```json5 + { + "version": + } + ``` + +- **Output** + + ```json5 + {} + ``` + +- **Errors** + + - the version is outdated + - the tag does not exist + - privileges are too low + +- **Description** + + Deletes existing tag. The tag to be deleted must have no usages. + +## Merging tags +- **Request** + + `POST /tag-merge/` + +- **Input** + + ```json5 + { + "removeVersion": , + "remove": , + "mergeToVersion": , + "mergeTo": + } + ``` + +- **Output** + + A [tag resource](#tag) containing the merged tag. + +- **Errors** + + - the version of either tag is outdated + - the source or target tag does not exist + - the source tag is the same as the target tag + - privileges are too low + +- **Description** + + Removes source tag and merges all of its usages, suggestions and + implications to the target tag. Other tag properties such as category and + aliases do not get transferred and are discarded. + +## Listing tag siblings +- **Request** + + `GET /tag-siblings/` + +- **Output** + + ```json5 + { + "results": [ + { + "tag": , + "occurrences": + }, + { + "tag": , + "occurrences": + } + ] + } + ``` + ...where `` is a [tag resource](#tag). + +- **Errors** + + - privileges are too low + +- **Description** + + Lists siblings of given tag, e.g. tags that were used in the same posts as + the given tag. `occurrences` field signifies how many times a given sibling + appears with given tag. Results are sorted by occurrences count and the + list is truncated to the first 50 elements. Doesn't use paging. + +## Listing posts +- **Request** + + `GET /posts/?offset=&limit=&query=` + +- **Output** + + A [paged search result resource](#paged-search-result), for which + `` is a [post resource](#post). + +- **Errors** + + - privileges are too low + +- **Description** + + Searches for posts. + + **Anonymous tokens** + + Same as `tag` token. + + **Named tokens** + + | `` | Description | + | -------------------- | ---------------------------------------------------------- | + | `id` | having given post number | + | `tag` | having given tag (accepts wildcards) | + | `score` | having given score | + | `uploader` | uploaded by given user (accepts wildcards) | + | `upload` | alias of upload | + | `submit` | alias of upload | + | `comment` | commented by given user (accepts wildcards) | + | `fav` | favorited by given user (accepts wildcards) | + | `tag-count` | having given number of tags | + | `comment-count` | having given number of comments | + | `fav-count` | favorited by given number of users | + | `note-count` | having given number of annotations | + | `note-text` | having given note text (accepts wildcards) | + | `relation-count` | having given number of relations | + | `feature-count` | having been featured given number of times | + | `type` | given type of posts. `` can be either `image`, `animation` (or `animated` or `anim`), `flash` (or `swf`) or `video` (or `webm`). | + | `content-checksum` | having given SHA1 checksum | + | `file-size` | having given file size (in bytes) | + | `image-width` | having given image width (where applicable) | + | `image-height` | having given image height (where applicable) | + | `image-area` | having given number of pixels (image width * image height) | + | `image-aspect-ratio` | having given aspect ratio (image width / image height) | + | `image-ar` | alias of `image-aspect-ratio` | + | `width` | alias of `image-width` | + | `height` | alias of `image-height` | + | `area` | alias of `image-area` | + | `ar` | alias of `image-aspect-ratio` | + | `aspect-ratio` | alias of `image-aspect-ratio` | + | `creation-date` | posted at given date | + | `creation-time` | alias of `creation-date` | + | `date` | alias of `creation-date` | + | `time` | alias of `creation-date` | + | `last-edit-date` | edited at given date | + | `last-edit-time` | alias of `last-edit-date` | + | `edit-date` | alias of `last-edit-date` | + | `edit-time` | alias of `last-edit-date` | + | `comment-date` | commented at given date | + | `comment-time` | alias of `comment-date` | + | `fav-date` | last favorited at given date | + | `fav-time` | alias of `fav-date` | + | `feature-date` | featured at given date | + | `feature-time` | alias of `feature-time` | + | `safety` | having given safety. `` can be either `safe`, `sketchy` (or `questionable`) or `unsafe`. | + | `rating` | alias of `safety` | + + **Sort style tokens** + + | `` | Description | + | ---------------- | ------------------------------------------------ | + | `random` | as random as it can get | + | `id` | highest to lowest post number | + | `score` | highest scored | + | `tag-count` | with most tags | + | `comment-count` | most commented first | + | `fav-count` | loved by most | + | `note-count` | with most annotations | + | `relation-count` | with most relations | + | `feature-count` | most often featured | + | `file-size` | largest files first | + | `image-width` | widest images first | + | `image-height` | tallest images first | + | `image-area` | largest images first | + | `width` | alias of `image-width` | + | `height` | alias of `image-height` | + | `area` | alias of `image-area` | + | `creation-date` | newest to oldest (pretty much same as id) | + | `creation-time` | alias of `creation-date` | + | `date` | alias of `creation-date` | + | `time` | alias of `creation-date` | + | `last-edit-date` | like creation-date, only looks at last edit time | + | `last-edit-time` | alias of `last-edit-date` | + | `edit-date` | alias of `last-edit-date` | + | `edit-time` | alias of `last-edit-date` | + | `comment-date` | recently commented by anyone | + | `comment-time` | alias of `comment-date` | + | `fav-date` | recently added to favorites by anyone | + | `fav-time` | alias of `fav-date` | + | `feature-date` | recently featured | + | `feature-time` | alias of `feature-time` | + + **Special tokens** + + | `` | Description | + | ------------ | ------------------------------------------------------------- | + | `liked` | posts liked by currently logged in user | + | `disliked` | posts disliked by currently logged in user | + | `fav` | posts added to favorites by currently logged in user | + | `tumbleweed` | posts with score of 0, without comments and without favorites | + +## Creating post +- **Request** + + `POST /posts/` + +- **Input** + + ```json5 + { + "tags": [, , ], + "safety": , + "source": , // optional + "relations": [, , ], // optional + "notes": [, , ], // optional + "flags": [, ], // optional + "anonymous": // optional + } + ``` + +- **Files** + + - `content` - the content of the post. + - `thumbnail` - the content of custom thumbnail (optional). + +- **Output** + + A [post resource](#post). + +- **Errors** + + - tags have invalid names + - safety, notes or flags are invalid + - relations refer to non-existing posts + - privileges are too low + +- **Description** + + Creates a new post. If specified tags do not exist yet, they will be + automatically created. Tags created automatically have no implications, no + suggestions, one name and their category is set to the first tag category + found. Safety must be any of `"safe"`, `"sketchy"` or `"unsafe"`. Relations + must contain valid post IDs. `` currently can be only `"loop"` to + enable looping for video posts. Sending empty `thumbnail` will cause the + post to use default thumbnail. If `anonymous` is set to truthy value, the + uploader name won't be recorded (privilege verification still applies; it's + possible to disallow anonymous uploads completely from config.) For details + how to pass `content` and `thumbnail`, see [file uploads](#file-uploads). + +## Updating post +- **Request** + + `PUT /post/` + +- **Input** + + ```json5 + { + "version": , + "tags": [, , ], // optional + "safety": , // optional + "source": , // optional + "relations": [, , ], // optional + "notes": [, , ], // optional + "flags": [, ] // optional + } + ``` + +- **Files** + + - `content` - the content of the post (optional). + - `thumbnail` - the content of custom thumbnail (optional). + +- **Output** + + A [post resource](#post). + +- **Errors** + + - the version is outdated + - tags have invalid names + - safety, notes or flags are invalid + - relations refer to non-existing posts + - privileges are too low + +- **Description** + + Updates existing post. If specified tags do not exist yet, they will be + automatically created. Tags created automatically have no implications, no + suggestions, one name and their category is set to the first tag category + found. Safety must be any of `"safe"`, `"sketchy"` or `"unsafe"`. Relations + must contain valid post IDs. `` currently can be only `"loop"` to + enable looping for video posts. Sending empty `thumbnail` will reset the + post thumbnail to default. For details how to pass `content` and + `thumbnail`, see [file uploads](#file-uploads). All fields except the + [`version`](#versioning) are optional - update concerns only provided + fields. + +## Getting post +- **Request** + + `GET /post/` + +- **Output** + + A [post resource](#post). + +- **Errors** + + - the post does not exist + - privileges are too low + +- **Description** + + Retrieves information about an existing post. + +## Deleting post +- **Request** + + `DELETE /post/` + +- **Input** + + ```json5 + { + "version": + } + ``` + +- **Output** + + ```json5 + {} + ``` + +- **Errors** + + - the version is outdated + - the post does not exist + - privileges are too low + +- **Description** + + Deletes existing post. Related posts and tags are kept. + +## Merging posts +- **Request** + + `POST /post-merge/` + +- **Input** + + ```json5 + { + "removeVersion": , + "remove": , + "mergeToVersion": , + "mergeTo": , + "replaceContent": + } + ``` + +- **Output** + + A [post resource](#post) containing the merged post. + +- **Errors** + + - the version of either post is outdated + - the source or target post does not exist + - the source post is the same as the target post + - privileges are too low + +- **Description** + + Removes source post and merges all of its tags, relations, scores, + favorites and comments to the target post. If `replaceContent` is set to + true, content of the target post is replaced using the content of the + source post; otherwise it remains unchanged. Source post properties such as + its safety, source, whether to loop the video and other scalar values do + not get transferred and are discarded. + +## Rating post +- **Request** + + `PUT /post//score` + +- **Input** + + ```json5 + { + "score": + } + ``` + +- **Output** + + A [post resource](#post). + +- **Errors** + + - post does not exist + - score is invalid + - privileges are too low + +- **Description** + + Updates score of authenticated user for given post. Valid scores are -1, 0 + and 1. + +## Adding post to favorites +- **Request** + + `POST /post//favorite` + +- **Output** + + A [post resource](#post). + +- **Errors** + + - post does not exist + - privileges are too low + +- **Description** + + Marks the post as favorite for authenticated user. + +## Removing post from favorites +- **Request** + + `DELETE /post//favorite` + +- **Output** + + A [post resource](#post). + +- **Errors** + + - post does not exist + - privileges are too low + +- **Description** + + Unmarks the post as favorite for authenticated user. + +## Getting featured post +- **Request** + + `GET /featured-post` + +- **Output** + + A [post resource](#post). + +- **Errors** + + - privileges are too low + +- **Description** + + Retrieves the post that is currently featured on the main page in web + client. If no post is featured, `` is null. Note that this method + exists mostly for compatibility with setting featured post - most of times, + you'd want to use query global info which contains more information. + +## Featuring post +- **Request** + + `POST /featured-post` + +- **Input** + + ```json5 + { + "id": + } + ``` + +- **Output** + + A [post resource](#post). + +- **Errors** + + - privileges are too low + - trying to feature a post that is currently featured + +- **Description** + + Features a post on the main page in web client. + +## Reverse image search +- **Request** + + `POST /posts/reverse-search` + +- **Files** + + - `content` - the image to search for. + +- **Output** + + An [image search result](#image-search-result). + +- **Errors** + + - privileges are too low + +- **Description** + + Retrieves posts that look like the input image. + +## Listing comments +- **Request** + + `GET /comments/?offset=&limit=&query=` + +- **Output** + + A [paged search result resource](#paged-search-result), for which + `` is a [comment resource](#comment). + +- **Errors** + + - privileges are too low + +- **Description** + + Searches for comments. + + **Anonymous tokens** + + Same as `text` token. + + **Named tokens** + + | `` | Description | + | ---------------- | ---------------------------------------------- | + | `id` | specific comment ID | + | `post` | specific post ID | + | `user` | created by given user (accepts wildcards) | + | `author` | alias of `user` | + | `text` | containing given text (accepts wildcards) | + | `creation-date` | created at given date | + | `creation-time` | alias of `creation-date` | + | `last-edit-date` | whose most recent edit date matches given date | + | `last-edit-time` | alias of `last-edit-date` | + | `edit-date` | alias of `last-edit-date` | + | `edit-time` | alias of `last-edit-date` | + + **Sort style tokens** + + | `` | Description | + | ---------------- | ------------------------- | + | `random` | as random as it can get | + | `user` | author name, A to Z | + | `author` | alias of `user` | + | `post` | post ID, newest to oldest | + | `creation-date` | newest to oldest | + | `creation-time` | alias of `creation-date` | + | `last-edit-date` | recently edited first | + | `last-edit-time` | alias of `last-edit-date` | + | `edit-date` | alias of `last-edit-date` | + | `edit-time` | alias of `last-edit-date` | + + **Special tokens** + + None. + +## Creating comment +- **Request** + + `POST /comments/` + +- **Input** + + ```json5 + { + "text": , + "postId": + } + ``` + +- **Output** + + A [comment resource](#comment). + +- **Errors** + + - the post does not exist + - comment text is empty + - privileges are too low + +- **Description** + + Creates a new comment under given post. + +## Updating comment +- **Request** + + `PUT /comment/` + +- **Input** + + ```json5 + { + "version": , + "text": // mandatory + } + ``` + +- **Output** + + A [comment resource](#comment). + +- **Errors** + + - the version is outdated + - the comment does not exist + - new comment text is empty + - privileges are too low + +- **Description** + + Updates an existing comment text. + +## Getting comment +- **Request** + + `GET /comment/` + +- **Output** + + A [comment resource](#comment). + +- **Errors** + + - the comment does not exist + - privileges are too low + +- **Description** + + Retrieves information about an existing comment. + +## Deleting comment +- **Request** + + `DELETE /comment/` + +- **Input** + + ```json5 + { + "version": + } + ``` + +- **Output** + + ```json5 + {} + ``` + +- **Errors** + + - the version is outdated + - the comment does not exist + - privileges are too low + +- **Description** + + Deletes existing comment. + +## Rating comment +- **Request** + + `PUT /comment//score` + +- **Input** + + ```json5 + { + "score": + } + ``` + +- **Output** + + A [comment resource](#comment). + +- **Errors** + + - comment does not exist + - score is invalid + - privileges are too low + +- **Description** + + Updates score of authenticated user for given comment. Valid scores are -1, + 0 and 1. + +## Listing users +- **Request** + + `GET /users/?offset=&limit=&query=` + +- **Output** + + A [paged search result resource](#paged-search-result), for which + `` is a [user resource](#user). + +- **Errors** + + - privileges are too low + +- **Description** + + Searches for users. + + **Anonymous tokens** + + Same as `name` token. + + **Named tokens** + + | `` | Description | + | ----------------- | ----------------------------------------------- | + | `name` | having given name (accepts wildcards) | + | `creation-date` | registered at given date | + | `creation-time` | alias of `creation-date` | + | `last-login-date` | whose most recent login date matches given date | + | `last-login-time` | alias of `last-login-date` | + | `login-date` | alias of `last-login-date` | + | `login-time` | alias of `last-login-date` | + + **Sort style tokens** + + | `` | Description | + | ----------------- | -------------------------- | + | `random` | as random as it can get | + | `name` | A to Z | + | `creation-date` | newest to oldest | + | `creation-time` | alias of `creation-date` | + | `last-login-date` | recently active first | + | `last-login-time` | alias of `last-login-date` | + | `login-date` | alias of `last-login-date` | + | `login-time` | alias of `last-login-date` | + + **Special tokens** + + None. + +## Creating user +- **Request** + + `POST /users` + +- **Input** + + ```json5 + { + "name": , + "password": , + "email": , // optional + "rank": , // optional + "avatarStyle": // optional + } + ``` + +- **Files** + + - `avatar` - the content of the new avatar (optional). + +- **Output** + + A [user resource](#user). + +- **Errors** + + - a user with such name already exists (names are case insensitive) + - either user name, password, email or rank are invalid + - the user is trying to update their or someone else's rank to higher than + their own + - avatar is missing for manual avatar style + - privileges are too low + +- **Description** + + Creates a new user using specified parameters. Names and passwords must + match `user_name_regex` and `password_regex` from server's configuration, + respectively. Email address, rank and avatar fields are optional. Avatar + style can be either `gravatar` or `manual`. `manual` avatar style requires + client to pass also `avatar` file - see [file uploads](#file-uploads) for + details. If the rank is empty and the user happens to be the first user + ever created, become an administrator, whereas subsequent users will be + given the rank indicated by `default_rank` in the server's configuration. + +## Updating user +- **Request** + + `PUT /user/` + +- **Input** + + ```json5 + { + "version": , + "name": , // optional + "password": , // optional + "email": , // optional + "rank": , // optional + "avatarStyle": // optional + } + ``` + +- **Files** + + - `avatar` - the content of the new avatar (optional). + +- **Output** + + A [user resource](#user). + +- **Errors** + + - the version is outdated + - the user does not exist + - a user with new name already exists (names are case insensitive) + - either user name, password, email or rank are invalid + - the user is trying to update their or someone else's rank to higher than + their own + - avatar is missing for manual avatar style + - privileges are too low + +- **Description** + + Updates an existing user using specified parameters. Names and passwords + must match `user_name_regex` and `password_regex` from server's + configuration, respectively. All fields are optional - update concerns only + provided fields. To update last login time, see + [authentication](#authentication). Avatar style can be either `gravatar` or + `manual`. `manual` avatar style requires client to pass also `avatar` + file - see [file uploads](#file-uploads) for details. All fields except the + [`version`](#versioning) are optional - update concerns only provided + fields. + +## Getting user +- **Request** + + `GET /user/` + +- **Output** + + A [user resource](#user). + +- **Errors** + + - the user does not exist + - privileges are too low + +- **Description** + + Retrieves information about an existing user. + +## Deleting user +- **Request** + + `DELETE /user/` + +- **Input** + + ```json5 + { + "version": + } + ``` + +- **Output** + + ```json5 + {} + ``` + +- **Errors** + + - the version is outdated + - the user does not exist + - privileges are too low + +- **Description** + + Deletes existing user. + +## Listing user tokens +- **Request** + + `GET /user-tokens/` + +- **Output** + + An [unpaged search result resource](#unpaged-search-result), for which + `` is a [user token resource](#user-token). + +- **Errors** + + - privileges are too low + +- **Description** + + Searches for user tokens for the given user. + +## Creating user token +- **Request** + + `POST /user-token/` + +- **Input** + + ```json5 + { + "enabled": , // optional + "note": , // optional + "expirationTime": // optional + } + ``` + +- **Output** + + A [user token resource](#user-token). + +- **Errors** + + - privileges are too low + +- **Description** + + Creates a new user token that can be used for authentication of API + endpoints instead of a password. + +## Updating user token +- **Request** + + `PUT /user-token//` + +- **Input** + + ```json5 + { + "version": , + "enabled": , // optional + "note": , // optional + "expirationTime": // optional + } + ``` + +- **Output** + + A [user token resource](#user-token). + +- **Errors** + + - the version is outdated + - the user token does not exist + - privileges are too low + +- **Description** + + Updates an existing user token using specified parameters. All fields + except the [`version`](#versioning) are optional - update concerns only + provided fields. + +## Deleting user token +- **Request** + + `DELETE /user-token//` + +- **Input** + + ```json5 + { + "version": + } + ``` + +- **Output** + + ```json5 + {} + ``` + +- **Errors** + + - the token does not exist + - privileges are too low + +- **Description** + + Deletes existing user token. + +## Password reset - step 1: mail request +- **Request** + + `GET /password-reset/` + +- **Output** + + ``` + {} + ``` + +- **Errors** + + - the user does not exist + - the user hasn't provided an email address + +- **Description** + + Sends a confirmation email to given user. The email contains link + containing a token. The token cannot be guessed, thus using such link + proves that the person who requested to reset the password also owns the + mailbox, which is a strong indication they are the rightful owner of the + account. + +## Password reset - step 2: confirmation +- **Request** + + `POST /password-reset/` + +- **Input** + + ```json5 + { + "token": + } + ``` + +- **Output** + + ```json5 + { + "password": + } + ``` + +- **Errors** + + - the token is missing + - the token is invalid + - the user does not exist + +- **Description** + + Generates a new password for given user. Password is sent as plain-text, so + it is recommended to connect through HTTPS. + +## Listing snapshots +- **Request** + + `GET /snapshots/?offset=&limit=&query=` + +- **Output** + + A [paged search result resource](#paged-search-result), for which + `` is a [snapshot resource](#snapshot). + +- **Errors** + + - privileges are too low + +- **Description** + + Lists recent resource snapshots. + + **Anonymous tokens** + + Not supported. + + **Named tokens** + + | `` | Description | + | ----------------- | ---------------------------------------------------------------- | + | `type` | involving given resource type | + | `id` | involving given resource id | + | `date` | created at given date | + | `time` | alias of `date` | + | `operation` | `modified`, `created`, `deleted` or `merged` | + | `user` | name of the user that created given snapshot (accepts wildcards) | + + **Sort style tokens** + + None. The snapshots are always sorted by creation time. + + **Special tokens** + + None. + +## Getting global info +- **Request** + + `GET /info` + +- **Output** + + ```json5 + { + "postCount": , + "diskUsage": , // in bytes + "featuredPost": , + "featuringTime":