diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml
new file mode 100644
index 0000000..b77d9d5
--- /dev/null
+++ b/.github/workflows/go-test.yml
@@ -0,0 +1,31 @@
+name: Upload Go test results
+# https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-go
+on: [push]
+permissions:
+ contents: read
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ go-version: [ '1.24.3' ]
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: ${{ matrix.go-version }}
+ - name: Install dependencies
+ run: go get .
+ working-directory: ./src
+ - name: Test with Go
+ run: go test -json > TestResults-${{ matrix.go-version }}.json
+ working-directory: ./src
+ - name: Upload Go test results
+ uses: actions/upload-artifact@v4
+ with:
+ name: Go-results-${{ matrix.go-version }}
+ path: src/TestResults-${{ matrix.go-version }}.json
diff --git a/README.md b/README.md
index a04e13d..9d25b06 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# e6-cache
-**e6-cache** is a locally hosted caching proxy for e621 (or anything that's api compatible), designed to passively archive and cache content you browse.
+**e6-cache** is a locally hosted caching proxy for e621 (or anything that's api compatible), designed to passively archive and cache content as you browse.
## Why?
@@ -9,7 +9,7 @@ Because:
* You don’t remember that *one* post with the perfect lighting and suspiciously specific tags
* **You want your own archive. Your own CDN. Your own sin-server.**
* You have too much storage space
-* Your Internet connection is too slow (the only legitimate use case)
+* Your Internet connection is too slow
* You worry about e621 going down, or posts being removed.
## What it does
@@ -24,7 +24,7 @@ Because:
* **Transparent Proxy**: Redirects all requests through e6-cache, then to your chosen instance.
* **Passive Caching**: Automatically caches every post you view.
* **Local Storage**: Stores metadata in a local PostgreSQL database and media files in your own S3-compatible bucket.
-* **Customizable**: Easily configurable to work with any e621-compatible instance.
+* **Fast**: Streams the images / videos directly to your client.
* **Self-Hosted**: Runs on your own server, giving you full control over your data.
* **Authentication**: Supports authentication for secure access, even when exposed to the world.
@@ -64,20 +64,20 @@ After the container is running, you can access the API at `http://localhost:8080
For most users i recommend using [e1547](https://github.com/clragon/e1547) as it has built-in support for custom instances.
-### e1547 Setup
+### e1547
> [!IMPORTANT]
> You need to have an public URL for the API to work with e1547, as it requires https.
https://github.com/user-attachments/assets/d2304e64-0c08-4065-bd55-aaa24d13727e
-### The Wolf's Stash Setup
+### The Wolf's Stash
For The Wolf's Stash, it just reports the host not being supported.
### Other Clients
Feel free to open a PR to add documentation for other clients.
-## Speed Comparison (Speed depends on your internet, and database speed)
+## Speed Comparison
### Image 1:
- **Normal e621 image load:** 2.859s
@@ -94,21 +94,9 @@ Feel free to open a PR to add documentation for other clients.
* Proxy Mode (it act's like a proxy and redirects all e621 requests to e6-cache)
* Firefox Extension (to make it easier to use by replacing all e621 links with e6-cache links)
-* Offline Mode (to use the cache without internet connection, like a local e621)
+* Offline API Mode (to use the cache without internet connection, like a local e621)
* Website (basically a mirror of e621, but with the cache enabled)
-## Architecture
-
-* Every API request you make goes through `e6-cache`
-* We pull the data from the API and store it in our database. And then we serve it to you
-* If there are images, we create special proxy links
-* If the user or a client accesses the proxy link, we check if the image is already cached, and if not, we download it and store it in the S3 bucket
-
-## Ethical & Legal Notice
-
-This project **does not** scrape, spider, or hammer the API. It only caches what *you* manually request.
-You, as the operator, are fully responsible for your usage. This just hands you a shovel. What you dig up is on you.
-
## Contributing
Feel free to open an issue or a pull request. I don't have any specific guidelines for contributing, just be nice and respectful.
@@ -116,3 +104,8 @@ Feel free to open an issue or a pull request. I don't have any specific guidelin
## Why?
Idk. I just wanted to play with Go, Docker and wanted to make something (relatively) useful. It's also my first published project on GitHub.
+
+## Ethical & Legal Notice
+
+This project **does not** scrape, spider, or hammer the API. It only caches what *you* manually request.
+You, as the operator, are fully responsible for your usage. This just hands you a shovel. What you dig up is on you.
\ No newline at end of file
diff --git a/THIRD_PARTY.md b/THIRD_PARTY.md
new file mode 100644
index 0000000..9d4df6c
--- /dev/null
+++ b/THIRD_PARTY.md
@@ -0,0 +1,5 @@
+# Third Party Libraries / Assets
+
+This project wouldn't be possible without the following third party libraries and assets:
+
+- e621 OpenAPI Specification from [https://github.com/DonovanDMC/E621OpenAPI](https://github.com/DonovanDMC/E621OpenAPI) (License: MIT)
\ No newline at end of file
diff --git a/development.md b/development.md
new file mode 100644
index 0000000..3964319
--- /dev/null
+++ b/development.md
@@ -0,0 +1,23 @@
+
+## Development
+`e6-cache` works by forwarding the users request, which means that it should never require any client changes, and should work on anything that abides by the offical api.
+
+## Request Forwarding Process
+The core concept of the implementation looks like this:
+
+1. Receive an api request
+2. Forward the request to the target e6-based service (While checking for the Proxy Auth for example)
+3. Capture the response
+4. Modify the response and save it in the DB (URIs dont change in the DB)
+5. Return the modified response to the client
+
+## File Proxying Process
+File Proxying works like this:
+
+1. Check the Signature and decode the base64 encoded url
+2. Check in S3 if the file exists
+3. If not, then request it and save it while forwarding it to the client. If it exist than stream it to the client from S3.
+
+## OpenAPI Updates
+The `update_openapi.sh` script:
+- Updates the openai.yaml file from another repo
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index ed29f13..97b4571 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -41,7 +41,7 @@ services:
# Proxy settings
PROXY_URL: http://localhost:8080 # Set this to the Server IP / URL, as otherwise the proxy will not work.
E6_BASE: https://e621.net
- PROXY_AUTH: "" # Leave empty to disable proxy auth append like this to your username "Username:YourPassword"
+ PROXY_AUTH: "" # Leave empty to disable proxy auth. If you want to use it, append like this to your username "Username:YourProxyPassword"
ports:
- "8080:8080" # Point this to an Reverse Proxy and set the Proxy Url acordingly.
diff --git a/src/.env.example b/src/.env.example
index 7954167..58c5cc5 100644
--- a/src/.env.example
+++ b/src/.env.example
@@ -17,4 +17,4 @@ S3_REGION=us-east-1
# Proxy settings
PROXY_URL=http://localhost:8080
E6_BASE=https://e621.net
-PROXY_AUTH="" # Leave empty to disable proxy auth append like this to your username "Username:YourPassword"
\ No newline at end of file
+PROXY_AUTH="" # Leave empty to disable proxy auth. If you want to use it, append like this to your username "Username:YourProxyPassword"
\ No newline at end of file
diff --git a/src/db.go b/src/db.go
index 222fc6c..fed58f6 100644
--- a/src/db.go
+++ b/src/db.go
@@ -5,7 +5,6 @@ import (
"context"
"database/sql"
"fmt"
- "log"
"strings"
"time"
@@ -80,23 +79,23 @@ func (d *DB) CreatePost(ctx context.Context, p *Post) error {
)
if err != nil {
- logging.Error("Error inserting post: ", err)
+ logging.Error("Error inserting post: %v", err)
}
return err
}
func (d *DB) CheckAndInsertPost(ctx context.Context, p *Post) error {
- logging.Info("Checking if post exists: ", p.ID)
+ logging.Info("Checking if post exists: %v", p.ID)
const existsQuery = `SELECT 1 FROM posts WHERE id = $1`
row := d.db.QueryRowContext(ctx, existsQuery, p.ID)
var dummy int
err := row.Scan(&dummy)
switch {
case err == sql.ErrNoRows:
- logging.Info("Post does not exist, inserting: ", p.ID)
+ logging.Error("Post does not exist, inserting: %v", p.ID)
return d.CreatePost(ctx, p)
case err != nil:
- log.Println("Error checking post existence: ", err)
+ logging.Error("Error checking post existence: %v", err)
return err
default:
// Row exists, nothing to do
@@ -143,7 +142,7 @@ func (d *DB) SaveComments(comments []Comment) error {
c.UpdaterName,
)
if err != nil {
- logging.Error(err.Error())
+ logging.Error("%v", err.Error())
return err
}
}
@@ -227,7 +226,7 @@ func (d *DB) UpdatePost(ctx context.Context, p *Post) error {
p.ApproverID, p.UploaderID, p.Description, p.CommentCount, p.IsFavorited,
)
if err != nil {
- logging.Error("Error updating post: ", err)
+ logging.Error("Error updating post: %v", err)
}
return err
}
@@ -236,7 +235,7 @@ func (d *DB) UpdatePost(ctx context.Context, p *Post) error {
func (d *DB) DeletePost(ctx context.Context, id int64) error {
_, err := d.db.ExecContext(ctx, `DELETE FROM posts WHERE id = $1`, id)
if err != nil {
- logging.Error("Error deleting post: ", err)
+ logging.Error("Error deleting post: %v", err)
}
return err
}
@@ -271,20 +270,20 @@ func (d *DB) UpdatePool(ctx context.Context, p *Pool) error {
p.Description, p.IsActive, p.Category, p.PostCount,
)
if err != nil {
- logging.Error("error upserting pool: ", err)
+ logging.Error("error upserting pool: %v", err)
return err
}
_, err = tx.ExecContext(ctx, `DELETE FROM pool_posts WHERE pool_id = $1`, p.ID)
if err != nil {
- logging.Error("error clearing pool_posts: ", err)
+ logging.Error("error clearing pool_posts: %v", err)
return err
}
for _, postID := range p.PostIDs {
_, err = tx.ExecContext(ctx, `INSERT INTO pool_posts (pool_id, post_id) VALUES ($1, $2)`, p.ID, postID)
if err != nil {
- logging.Error("error inserting pool_post: ", err)
+ logging.Error("error inserting pool_post: %v", err)
return err
}
}
diff --git a/src/go.mod b/src/go.mod
index 26b1c71..8a22778 100644
--- a/src/go.mod
+++ b/src/go.mod
@@ -9,6 +9,7 @@ require (
github.com/aws/aws-sdk-go-v2/credentials v1.17.67
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.76
github.com/aws/aws-sdk-go-v2/service/s3 v1.79.4
+ github.com/getkin/kin-openapi v0.132.0
github.com/gin-gonic/gin v1.10.1
github.com/joho/godotenv v1.5.1
github.com/lib/pq v1.10.9
@@ -35,17 +36,25 @@ require (
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
+ github.com/go-openapi/jsonpointer v0.21.0 // indirect
+ github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
+ github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
+ github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
+ github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
+ github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
+ github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.8.0 // indirect
diff --git a/src/go.sum b/src/go.sum
index 11bfdba..a1730ed 100644
--- a/src/go.sum
+++ b/src/go.sum
@@ -51,10 +51,16 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk=
+github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
+github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
+github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
+github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@@ -70,6 +76,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -80,6 +88,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -87,8 +97,16 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
+github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
+github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
+github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
+github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
+github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
+github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
diff --git a/src/http_calls.go b/src/http_calls.go
index aa53411..1fa1934 100644
--- a/src/http_calls.go
+++ b/src/http_calls.go
@@ -10,6 +10,7 @@ import (
"context"
"encoding/base64"
"encoding/json"
+ "fmt"
"io"
"net/http"
"path/filepath"
@@ -46,15 +47,14 @@ func makeProxyLink(original string) string {
// if the route changes, we need to update this
proxiedURL := PROXY_URL + "/proxy/" + encodedUrl + "?sig=" + sig
-
- logging.Info("Creating proxy url for file: ", original, " | ID: ", match[1], " | Proxied URL: ", proxiedURL)
+ logging.Info("Creating proxy url for file: %v | ID: %v | Proxied URL: %v", original, match[1], proxiedURL)
return proxiedURL
}
func proxyAndTransform(c *gin.Context) {
- logging.Debug("Headers: ", c.Request.Header)
+ logging.Debug("Headers: %v", c.Request.Header)
auth := c.Request.Header.Get("Authorization")
@@ -72,13 +72,14 @@ func proxyAndTransform(c *gin.Context) {
auth = strings.TrimPrefix(auth, "Basic ")
decodedAuth, err := base64.StdEncoding.DecodeString(auth)
if err != nil {
- logging.Error("Error decoding auth header: ", err)
+ logging.Error("Error decoding auth header: %v", err)
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
// parse it into username, password and proxy auth
authParts := strings.Split(string(decodedAuth), ":")
- suppliedProxyAuth := authParts[1] // if you want to check the password, set it to 2
+ suppliedProxyAuth := authParts[1] // to change the password position you need to change this here. 1 is after the username, 2 is after the proxy auth
+ logging.Debug("Parsed Proxy Authorization header: %v", authParts)
if len(authParts) != 3 || suppliedProxyAuth != PROXY_AUTH {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
@@ -86,8 +87,9 @@ func proxyAndTransform(c *gin.Context) {
c.Request.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(authParts[0]+":"+authParts[2])))
} else {
-
+ // this gets run if the proxy auth function not enabled
if auth == "" {
+ logging.Debug("No Authorization header found, using anonymous user")
requestUsername = "anonymous"
}
@@ -124,8 +126,8 @@ func proxyAndTransform(c *gin.Context) {
// useragent stuff
setUseragent(requestUsername, req)
- logging.Debug("Host: ", c.Request.Host)
- logging.Debug("Proxied Headers: ", req.Header)
+ logging.Debug("Host: %v", c.Request.Host)
+ logging.Debug("Proxied Headers: %v", req.Header)
// Perform request
resp, err := http.DefaultClient.Do(req)
@@ -172,11 +174,10 @@ func proxyAndTransform(c *gin.Context) {
defer reader.Close()
default:
+ logging.Debug("Server sent uncompressed response")
reader = resp.Body
}
- //Accept-Encoding:[gzip, compress, deflate, br]
-
// Read response body
respBody, err := io.ReadAll(reader)
@@ -184,13 +185,14 @@ func proxyAndTransform(c *gin.Context) {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "Failed to read response body"})
return
}
+
// check for rate limit
if resp.StatusCode == 501 {
c.AbortWithStatusJSON(http.StatusTooManyRequests, gin.H{"error": "Rate limit exceeded"})
return
}
- logging.Debug("Response Body: ", string(respBody))
+ logging.Debug("Response Body: %v", string(respBody))
switch {
case strings.HasSuffix(c.Request.URL.Path, "/comments.json") && c.Query("search[post_id]") != "": // specific post comments are returned differently
@@ -208,14 +210,14 @@ func proxyAndTransform(c *gin.Context) {
return
}
- logging.Info("Saving ", len(comments), " comments")
+ logging.Info("Saving %v comments", len(comments))
Database.SaveComments(comments)
respBody, _ = json.Marshal(comments)
case strings.HasSuffix(c.Request.URL.Path, "/posts.json") || strings.HasSuffix(c.Request.URL.Path, "/comments.json"): // comments and posts seem to be the same thing
var posts PostsResponse
if err := json.Unmarshal(respBody, &posts); err != nil {
- logging.Debug("Response Body: ", string(respBody))
+ logging.Debug("Response Body: %v", string(respBody))
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid response format", "ok": false})
return
}
@@ -230,7 +232,7 @@ func proxyAndTransform(c *gin.Context) {
if err := json.Unmarshal(respBody, &post); err != nil {
- logging.Debug("Response Body: ", string(respBody))
+ logging.Debug("Response Body: %v", string(respBody))
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid response format", "ok": false})
return
}
@@ -248,7 +250,7 @@ func proxyAndTransform(c *gin.Context) {
for _, pool := range pools {
// Store in DB
- ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
+ ctx, cancel := context.WithTimeout(context.Background(), globalTimeout)
defer cancel()
Database.UpdatePool(ctx, &pool)
}
@@ -263,7 +265,7 @@ func proxyAndTransform(c *gin.Context) {
}
// Store in DB
- ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
+ ctx, cancel := context.WithTimeout(context.Background(), globalTimeout)
defer cancel()
Database.UpdatePool(ctx, &pool)
@@ -275,7 +277,7 @@ func proxyAndTransform(c *gin.Context) {
}
func proxyFile(c *gin.Context) {
- fileID := c.Param("File_ID")
+ fileID := c.Param("fileId")
sig := c.Query("sig")
if sig == "" {
@@ -305,11 +307,11 @@ func proxyFile(c *gin.Context) {
c.Header("Expires", time.Now().Add(time.Duration(maxCacheAge)*time.Second).Format(http.TimeFormat))
if fileExists && err == nil {
- logging.Info("File exists in S3, downloading: ", string(url))
+ logging.Info("File exists in S3, downloading: %v", string(url))
body, err := S3.StreamFromS3(c, string(CleanFileID))
if err != nil {
- logging.Error("Error downloading from S3: ", err)
+ logging.Error("Error downloading from S3: %v", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "Failed to download from S3", "ok": false})
return
}
@@ -383,12 +385,12 @@ func proxyFile(c *gin.Context) {
// upload to S3 in the background, while the user is downloading the file
go func() {
- logging.Info("Uploading to S3: ", string(CleanFileID))
+ logging.Info("Uploading to S3: %v", string(CleanFileID))
err := S3.UploadToS3(c, r1, string(CleanFileID))
if err != nil {
- logging.Error("Failed to upload to S3", err)
+ logging.Error("Failed to upload to S3: %v", err)
}
- logging.Info("Upload to S3 complete: ", string(CleanFileID))
+ logging.Info("Upload to S3 complete: %v", string(CleanFileID))
}()
// Stream live to user (I hope it's actually streaming)
@@ -416,11 +418,11 @@ func copyHeaders(src http.Header, dst http.Header) {
}
func ProcessPost(c *gin.Context, post *Post) {
- ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
+ ctx, cancel := context.WithTimeout(context.Background(), globalTimeout)
defer cancel()
Database.CheckAndInsertPost(ctx, post)
- // Rewrite url to go through proxy
+ // Rewrite url to go through the proxy
post.File.URL = makeProxyLink(post.File.URL)
post.Preview.URL = makeProxyLink(post.Preview.URL)
post.Sample.URL = makeProxyLink(post.Sample.URL)
@@ -434,7 +436,7 @@ func setUseragent(username string, req *http.Request) {
return
}
- useragent = useragentBase + " (Request made on behalf of " + username + ")"
+ useragent = fmt.Sprintf("%s (Request made on behalf of %s)", useragentBase, username)
req.Header.Set("User-Agent", useragent)
}
diff --git a/src/main.go b/src/main.go
index 05f1285..e4e04e6 100644
--- a/src/main.go
+++ b/src/main.go
@@ -4,12 +4,19 @@ import (
"bugmaschine/e6-cache/logging"
"context"
_ "embed"
+ "fmt"
+ "log"
"os"
+ "regexp"
+ "slices"
"strconv"
+ "strings"
"time"
"bugmaschine/e6-cache/signer"
+ "github.com/getkin/kin-openapi/openapi3"
+
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
)
@@ -23,9 +30,10 @@ var (
Database DB
useragentBase = "e6-cache (https://github.com/bugmaschine/e6-cache)"
port = ":8080"
- Key []byte // gets randomly generated every launch, and used for signing the urls.
- maxCacheAge = 1 * time.Hour // idk what's a good value, but 1 hours seems enough
- Signer *signer.Signer // feel free to sugest a better name
+ Key []byte // gets randomly generated every launch, and used for signing the urls.
+ maxCacheAge = 1 * time.Hour // idk what's a good value, but 1 hours seems enough
+ Signer *signer.Signer // feel free to sugest a better name
+ globalTimeout = 5 * time.Second // global timeout for requests to e6, if it takes longer than this, we assume the request failed.
// env stuff
@@ -48,6 +56,9 @@ var (
PROXY_URL string
baseURL string
PROXY_AUTH string
+
+ //go:embed "openapi/e621.yaml"
+ e621OpenApiRoutes []byte // embedded OpenAPI routes, used to dynamically register the routes in the gin router.
)
func loadEnv() {
@@ -78,9 +89,9 @@ func loadEnv() {
PROXY_AUTH = os.Getenv("PROXY_AUTH")
if PROXY_AUTH != "" {
- logging.Debug("Proxy auth is enabled with key: ", PROXY_AUTH)
+ logging.Info("Proxy auth is enabled with key: %v", PROXY_AUTH)
} else {
- logging.Debug("Proxy auth is disabled")
+ logging.Info("Proxy auth is disabled")
}
}
@@ -94,13 +105,13 @@ func main() {
// generate signing key
Key = signer.GenerateSecretKey()
Signer = signer.NewSigner(Key)
- logging.Debug("Generated key: ", Key)
+ logging.Debug("Generated key: %v", Key)
// setup db
logging.Info("Connecting to DB...")
d, err := newDB(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_PORT)
if err != nil {
- logging.Info("Failed to connect to DB (is it up?): ", err)
+ logging.Info("Failed to connect to DB (is it up?): %v", err)
return
}
Database = d
@@ -108,11 +119,11 @@ func main() {
// setup s3
logging.Info("Connecting to S3...")
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ ctx, cancel := context.WithTimeout(context.Background(), globalTimeout)
defer cancel()
s3Svc, err := NewS3Service(ctx, S3_REGION, S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY, S3_BUCKET_NAME)
if err != nil {
- logging.Fatal("Failed to connect to S3: ", err)
+ logging.Fatal("Failed to connect to S3: %v", err)
}
S3 = *s3Svc
logging.Info("Connected to S3!")
@@ -124,47 +135,11 @@ func main() {
gin.SetMode(gin.ReleaseMode)
}
- router.ForwardedByClientIP = true
-
- // Routes implementing caching
-
- router.GET("/posts.json", proxyAndTransform)
- router.GET("/posts/:Post_ID.json", proxyAndTransform)
- router.GET("/pools.json", proxyAndTransform)
- router.GET("/pools/:Pool_ID", proxyAndTransform)
- router.GET("/comments.json", proxyAndTransform) // this for reason just returns posts?????????
-
- // Routes that could be maybe saved
-
- router.GET("/notes.json", proxyAndTransform)
- router.GET("/wiki_pages.json", proxyAndTransform)
- router.GET("/post_flags.json", proxyAndTransform)
-
- // Routes just needing proxying and have make no sense being cached
-
- router.POST("/favorites.json", proxyAndTransform)
- router.POST("/uploads.json", proxyAndTransform)
- router.POST("/post_flags.json", proxyAndTransform)
- router.DELETE("/favorites/:Post_ID", proxyAndTransform)
- router.POST("/notes.json", proxyAndTransform)
- router.PUT("/notes/*path", proxyAndTransform)
- router.DELETE("/notes/:Note_ID", proxyAndTransform)
- router.POST("/posts/:Post_ID/votes.json", proxyAndTransform)
- router.GET("/forum_topics.json", proxyAndTransform)
- router.GET("/forum_posts.json", proxyAndTransform)
- router.POST("/comments.json", proxyAndTransform)
- router.PUT("/comments/:Comment_ID", proxyAndTransform)
- router.DELETE("/comments/:Comment_ID", proxyAndTransform)
- router.POST("/comments/:Comment_ID/votes.json", proxyAndTransform)
- router.PUT("/users/:User_ID.json", proxyAndTransform)
- router.GET("/users/:User_ID.json", proxyAndTransform)
- router.GET("/tags/*path", proxyAndTransform)
-
- // potentially just forward to the baseUrl
- router.PATCH("/posts/:Post_ID", proxyAndTransform)
+ // register e621 routes
+ parseOpenAPIRoutes(e621OpenApiRoutes, router)
// Proxy files from S3, if not save them.
- router.GET("/proxy/:File_ID", proxyFile)
+ router.GET("/proxy/:fileId", proxyFile)
router.GET("/", func(c *gin.Context) {
c.String(200, "e6-cache is running. Use this as the instance in your preffered client.\n"+
@@ -172,7 +147,69 @@ func main() {
"Server is caching following url: "+baseURL)
})
- logging.Info("Started router at ", port)
+ logging.Info("Started router at %v", port)
router.Run(port)
+}
+
+func parseOpenAPIRoutes(openapifile []byte, router *gin.Engine) {
+
+ // load all routes from the file
+ loader := openapi3.NewLoader()
+ doc, err := loader.LoadFromData(openapifile)
+ if err != nil {
+ log.Fatalf("Failed to load OpenAPI: %v", err)
+ }
+
+ // create a regex to convert OpenAPI path parameters {id} to Gin's format :id
+ re := regexp.MustCompile(`\{(.+?)\}`)
+
+ // regex to remove a file extension from the end like .json, .png, etc.
+ extRegex := regexp.MustCompile(`\.[a-zA-Z0-9]+$`)
+
+ var registeredRoutes []string
+
+ for _, path := range doc.Paths.InMatchingOrder() {
+ pathItem := doc.Paths.Find(path)
+ if pathItem == nil {
+ logging.Warn("Path item not found for path: %v", path)
+ continue
+ }
+ logging.Debug("Processing path: %v", path)
+
+ // convert OpenAPI parameter syntax to Gin parameter syntax
+ convertedPath := re.ReplaceAllString(path, ":$1")
+
+ // get the parameter for later use
+ matches := re.FindStringSubmatch(path)
+ var param string
+ if len(matches) > 1 {
+ param = fmt.Sprintf(":%s", matches[1]) // this is $1
+ }
+
+ // remove leading slash
+ convertedPath = strings.TrimPrefix(convertedPath, "/")
+
+ // only remove the extension if it's not a parameter, otherwise it will break the route
+ // this is here to preserve cases like /posts.json
+ if strings.HasSuffix(extRegex.ReplaceAllString(convertedPath, ""), param) && param != "" {
+ convertedPath = extRegex.ReplaceAllString(convertedPath, "")
+ logging.Debug("Removed extension from path: %v", convertedPath)
+ } else {
+ logging.Debug("Preserving extension in path: %v because it's a parameter or doesn't have an extension", convertedPath)
+ }
+
+ if slices.Contains(registeredRoutes, convertedPath) {
+ logging.Warn("Duplicate route detected: %v", convertedPath)
+ continue
+ }
+
+ registeredRoutes = append(registeredRoutes, convertedPath)
+ for method := range pathItem.Operations() {
+ logging.Debug("Adding route: %v %v", method, convertedPath)
+ router.Handle(method, convertedPath, proxyAndTransform)
+ }
+ }
+
+ logging.Info("Registered %d routes from OpenAPI spec", len(doc.Paths.InMatchingOrder()))
}
diff --git a/src/openapi/e621.yaml b/src/openapi/e621.yaml
new file mode 100644
index 0000000..8afb089
--- /dev/null
+++ b/src/openapi/e621.yaml
@@ -0,0 +1,11428 @@
+openapi: "3.1.0"
+info:
+ title: "E621"
+ description: |
+ OpenAPI definition for E621's API. You can find the source [here](https://github.com/DonovanDMC/E621OpenAPI)
+ This document is intended to compliment E621's existing [API Documentation](https://e621.net/help/api).
+ Note if E621's api is under attack and/or cloudflare protections are enabled, the "Try it out" buttons here will not work.
+ If they are not working, you can check this [Unofficial Status Page](https://status.e621.ws).
+ license:
+ name: "MIT"
+ url: "https://opensource.org/licenses/MIT"
+
+ contact:
+ name: Donovan_DMC
+ url: https://e621.net/forum_topics/46279
+ version: "d69c34e"
+
+servers:
+ - url: https://e621.net
+ description: E621 (production)
+ - url: http://localhost:3000
+ description: E621 (Development)
+
+x-constants:
+ - &modaction-actions
+ - artist_page_rename
+ - artist_page_lock
+ - artist_page_unlock
+ - artist_user_linked
+ - artist_user_unlinked
+ - avoid_posting_create
+ - avoid_posting_update
+ - avoid_posting_delete
+ - avoid_posting_undelete
+ - avoid_posting_destroy
+ - blip_delete
+ - blip_hide
+ - blip_unhide
+ - blip_update
+ - comment_delete
+ - comment_hide
+ - comment_unhide
+ - comment_update
+ - forum_category_create
+ - forum_category_delete
+ - forum_category_update
+ - forum_post_delete
+ - forum_post_hide
+ - forum_post_unhide
+ - forum_post_update
+ - forum_topic_delete
+ - forum_topic_hide
+ - forum_topic_unhide
+ - forum_topic_lock
+ - forum_topic_unlock
+ - forum_topic_stick
+ - forum_topic_unstick
+ - forum_topic_update
+ - help_create
+ - help_delete
+ - help_update
+ - ip_ban_create
+ - ip_ban_delete
+ - mascot_create
+ - mascot_update
+ - mascot_delete
+ - pool_delete
+ - report_reason_create
+ - report_reason_delete
+ - report_reason_update
+ - set_update
+ - set_delete
+ - set_change_visibility
+ - tag_alias_create
+ - tag_alias_update
+ - tag_implication_create
+ - tag_implication_update
+ - ticket_claim
+ - ticket_unclaim
+ - ticket_update
+ - upload_whitelist_create
+ - upload_whitelist_update
+ - upload_whitelist_delete
+ - user_blacklist_changed
+ - user_text_change
+ - user_upload_limit_change
+ - user_flags_change
+ - user_level_change
+ - user_name_change
+ - user_delete
+ - user_ban
+ - user_ban_update
+ - user_unban
+ - user_feedback_create
+ - user_feedback_update
+ - user_feedback_delete
+ - user_feedback_undelete
+ - user_feedback_destroy
+ - wiki_page_rename
+ - wiki_page_delete
+ - wiki_page_lock
+ - wiki_page_unlock
+ - mass_update
+ - nuke_tag
+ - takedown_delete
+ - takedown_process
+ # legacy
+ - created_positive_record
+ - created_neutral_record
+ - created_negative_record
+ - created_flag_reason
+ - edited_flag_reason
+ - deleted_flag_reason
+ - post_move_favorites
+ - post_delete
+ - post_undelete
+ - post_destroy
+ - post_rating_lock
+ - post_unapprove
+ - post_replacement_accept
+ - post_replacement_reject
+ - post_replacement_delete
+ - &post-event-actions
+ - deleted
+ - undeleted
+ - approved
+ - unapproved
+ - flag_created
+ - flag_removed
+ - favorites_moved
+ - favorites_received
+ - rating_locked
+ - rating_unlocked
+ - status_locked
+ - status_unlocked
+ - note_locked
+ - note_unlocked
+ - comment_locked
+ - comment_unlocked
+ - replacement_accepted
+ - replacement_rejected
+ - replacement_promoted
+ - replacement_deleted
+ - expunged
+ - changed_bg_color
+ - &ratings
+ - s
+ - q
+ - e
+ - &tag-categories
+ - 0 # general
+ - 1 # artist
+ - 3 # copyright
+ - 4 # character
+ - 5 # species
+ - 6 # invalid
+ - 7 # meta
+ - 8 # lore
+ - &feedback-categories
+ - negative
+ - neutral
+ - positive
+ - &tag-request-statuses
+ - active
+ - deleted
+ - processing
+ - queued
+ - retired
+ - error
+ - pending
+ - &warning-types
+ - warning
+ - record
+ - ban
+ - &pool-categories
+ - collection
+ - series
+ - &ticket-types
+ - blip
+ - comment
+ - dmail
+ - forum
+ - pool
+ - post
+ - set
+ - user
+ - wiki
+ - &ticket-statuses
+ - pending
+ - partial
+ - approved
+
+components:
+ securitySchemes:
+ basicAuth:
+ type: http
+ scheme: basic
+ parameters:
+ id:
+ name: search[id]
+ in: query
+ description: Search for a specific id.
+ schema:
+ type: number
+ limit:
+ name: limit
+ in: query
+ description: The maximum number of results to return. Between 0 and 320.
+ schema:
+ type: number
+ minimum: 0
+ maximum: 500
+ page:
+ name: page
+ in: query
+ description: The page number of results to get. Between 1 and 750.
+ schema:
+ type: number
+ minimum: 1
+ maximum: 750
+ ip_addr:
+ name: search[ip_addr]
+ in: query
+ description: Must be Admin+ to use. See [postgres' documentation](https://www.postgresql.org/docs/9.3/functions-net.html) for information on how this is parsed. Specifically, "is contained within or equals" (`<<=`).
+ schema:
+ type: string
+ schemas:
+ Artist:
+ type: object
+ required:
+ - id
+ - name
+ - updated_at
+ - is_active
+ - other_names
+ - group_name
+ - linked_user_id
+ - created_at
+ - creator_id
+ - is_locked
+ - notes
+ - urls
+ properties:
+ id:
+ type: number
+ name:
+ type: string
+ updated_at:
+ type: string
+ format: date-time
+ is_active:
+ type: boolean
+ other_names:
+ type: array
+ items:
+ type: string
+ group_name:
+ type: string
+ linked_user_id:
+ type: ["null", "number"]
+ created_at:
+ type: string
+ format: date-time
+ creator_id:
+ type: number
+ is_locked:
+ type: boolean
+ notes:
+ type: ["null", "string"]
+ ArtistURL:
+ type: object
+ required:
+ - id
+ - artist_id
+ - url
+ - normalized_url
+ - created_at
+ - updated_at
+ - is_active
+ properties:
+ id:
+ type: number
+ artist_id:
+ type: number
+ url:
+ type: string
+ format: uri
+ normalized_url:
+ type: string
+ format: uri
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ is_active:
+ type: boolean
+ ArtistVersion:
+ type: object
+ required:
+ - id
+ - artist_id
+ - name
+ - updater_id
+ - created_at
+ - updated_at
+ - is_active
+ - other_names
+ - notes_changed
+ - urls
+ properties:
+ id:
+ type: number
+ artist_id:
+ type: number
+ name:
+ type: string
+ updater_id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ is_active:
+ type: boolean
+ other_names:
+ type: array
+ items:
+ type: string
+ notes_changed:
+ type: boolean
+ urls:
+ type: array
+ items:
+ type: string
+ format: uri
+ AvoidPosting:
+ type: object
+ required:
+ - id
+ - creator_id
+ - updater_id
+ - artist_id
+ - details
+ - is_active
+ - created_at
+ - updated_at
+ properties:
+ id:
+ type: number
+ creator_id:
+ type: number
+ updater_id:
+ type: number
+ artist_id:
+ type: number
+ staff_notes:
+ type: string
+ description: Only visible to Janitor+
+ details:
+ type: string
+ is_active:
+ type: boolean
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ AvoidPostingVersion:
+ type: object
+ required:
+ - id
+ - updater_id
+ - avoid_posting_id
+ - details
+ - is_active
+ - updated_at
+ properties:
+ id:
+ type: number
+ updater_id:
+ type: number
+ avoid_posting_id:
+ type: number
+ details:
+ type: string
+ staff_notes:
+ type: string
+ description: Only visible to Janitor+
+ is_active:
+ type: boolean
+ updated_at:
+ type: string
+ format: date-time
+ Ban:
+ type: object
+ required:
+ - id
+ - user_id
+ - reason
+ - expires_at
+ - banner_id
+ - created_at
+ - updated_at
+ properties:
+ id:
+ type: number
+ user_id:
+ type: number
+ reason:
+ type: string
+ expires_at:
+ type: ["string", "null"]
+ format: date-time
+ banner_id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ Blip:
+ type: object
+ required:
+ - id
+ - creator_id
+ - body
+ - response_to
+ - created_at
+ - updated_at
+ - is_hidden
+ - warning_type
+ - warning_user_id
+ - updater_id
+ - creator_name
+ properties:
+ id:
+ type: number
+ creator_id:
+ type: number
+ body:
+ type: string
+ response_to:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ is_hidden:
+ type: boolean
+ warning_type:
+ type: string
+ enum: *warning-types
+ warning_user_id:
+ type: number
+ updater_id:
+ type: number
+ creator_name:
+ type: string
+ BulkRelatedTag:
+ allOf:
+ - $ref: "#/components/schemas/RelatedTag"
+ - type: object
+ required:
+ - count
+ properties:
+ count:
+ type: number
+ BulkUpdateRequest:
+ type: object
+ required:
+ - id
+ - creator_id
+ - forum_topic_id
+ - script
+ - status
+ - created_at
+ - updated_at
+ - approver_id
+ - forum_post_id
+ - title
+ properties:
+ id:
+ type: number
+ creator_id:
+ type: number
+ forum_topic_id:
+ type: ["number", "null"]
+ script:
+ type: string
+ examples:
+ - "alias a -> b"
+ status:
+ type: string
+ enum:
+ - approved
+ - rejected
+ - pending
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ approver_id:
+ type: ["number", "null"]
+ forum_post_id:
+ type: ["number", "null"]
+ title:
+ type: string
+ Comment:
+ type: object
+ required:
+ - id
+ - created_at
+ - post_id
+ - creator_id
+ - body
+ - score
+ - updated_at
+ - updater_id
+ - do_not_bump_post
+ - is_hidden
+ - is_sticky
+ - warning_type
+ - warning_user_id
+ - creator_name
+ - updater_name
+ properties:
+ id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ post_id:
+ type: number
+ creator_id:
+ type: number
+ body:
+ type: string
+ score:
+ type: number
+ updated_at:
+ type: string
+ format: date-time
+ updater_id:
+ type: number
+ do_not_bump_post:
+ type: boolean
+ deprecated: true
+ is_hidden:
+ type: boolean
+ is_sticky:
+ type: boolean
+ warning_type:
+ type: ["string", "null"]
+ enum: *warning-types
+ warning_user_id:
+ type: ["number", "null"]
+ creator_name:
+ type: string
+ updater_name:
+ type: string
+ CurrentUser:
+ allOf:
+ - $ref: "#/components/schemas/User"
+ - type: object
+ required:
+ - blacklist_users
+ - description_collapsed_initially
+ - hide_comments
+ - show_hidden_comments
+ - show_post_statistics
+ - receive_email_notifications
+ - enable_keyboard_navigation
+ - enable_privacy_mode
+ - style_usernames
+ - enable_auto_complete
+ - disabled_cropped_thumbnails
+ - enable_safe_mode
+ - disable_responsive_mode
+ - no_flagging
+ - disable_user_dmails
+ - enable_compact_uploader
+ - replacements_beta
+ - updated_at
+ - email
+ - last_logged_in_at
+ - last_forum_read_at
+ - recent_tags
+ - comment_threshold
+ - default_image_size
+ - favorite_tags
+ - blacklisted_tags
+ - time_zone
+ - per_page
+ - custom_style
+ - favorite_count
+ - api_regen_multiplier
+ - api_burst_limit
+ - remaining_api_limit
+ - statement_timeout
+ - favorite_limit
+ - tag_query_limit
+ - has_mail
+ properties:
+ blacklist_users:
+ type: boolean
+ description_collapsed_initially:
+ type: boolean
+ hide_comments:
+ type: boolean
+ show_hidden_comments:
+ type: boolean
+ show_post_statistics:
+ type: boolean
+ receive_email_notifications:
+ type: boolean
+ enable_keyboard_navigation:
+ type: boolean
+ enable_privacy_mode:
+ type: boolean
+ style_usernames:
+ type: boolean
+ enable_auto_complete:
+ type: boolean
+ can_approve_posts:
+ type: boolean
+ disabled_cropped_thumbnails:
+ type: boolean
+ enable_safe_mode:
+ type: boolean
+ disable_responsive_mode:
+ type: boolean
+ no_flagging:
+ type: boolean
+ disable_user_dmails:
+ type: boolean
+ enable_compact_uploader:
+ type: boolean
+ replacements_beta:
+ type: boolean
+ updated_at:
+ type: string
+ format: date-time
+ email:
+ type: string
+ last_logged_in_at:
+ type: string
+ format: date-time
+ last_forum_read_at:
+ type: string
+ format: date-time
+ recent_tags:
+ type: string
+ comment_threshold:
+ type: number
+ default_image_sizedefault_image_size:
+ type: string
+ enum:
+ - large
+ - fit
+ - fitv
+ - original
+ favorite_tags:
+ type: string
+ blacklisted_tags:
+ type: string
+ time_zone:
+ type: string
+ per_page:
+ type: number
+ custom_style:
+ type: string
+ favorite_count:
+ type: number
+ api_regen_multiplier:
+ type: number
+ api_burst_limit:
+ type: number
+ remaining_api_limit:
+ type: number
+ statement_timeout:
+ type: number
+ favorite_limit:
+ type: number
+ tag_query_limit:
+ type: number
+ has_mail:
+ type: boolean
+ DMail:
+ type: object
+ required:
+ - id
+ - owner_id
+ - from_id
+ - to_id
+ - title
+ - body
+ - is_read
+ - is_deleted
+ - created_at
+ - updated_at
+ properties:
+ id:
+ type: number
+ owner_id:
+ type: number
+ from_id:
+ type: number
+ to_id:
+ type: number
+ title:
+ type: string
+ body:
+ type: string
+ is_read:
+ type: boolean
+ is_deleted:
+ type: boolean
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ DeferredPost:
+ type: object
+ required:
+ - id
+ - flags
+ - tags
+ - rating
+ - file_ext
+ - width
+ - height
+ - size
+ - created_at
+ - uploader
+ - uploader_id
+ - score
+ - fav_count
+ - is_favorited
+ - pools
+ - md5
+ - preview_url
+ - large_url
+ - file_url
+ - preview_width
+ - preview_height
+ properties:
+ id:
+ type: number
+ flags:
+ type: string
+ tags:
+ type: string
+ rating:
+ type: string
+ enum: *ratings
+ file_ext:
+ type: string
+ width:
+ type: number
+ height:
+ type: number
+ size:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ uploader:
+ type: string
+ uploader_id:
+ type: number
+ score:
+ type: number
+ fav_count:
+ type: number
+ is_favorited:
+ type: boolean
+ pools:
+ type: array
+ items:
+ type: number
+ md5:
+ type: string
+ preview_url:
+ type: ["string", "null"]
+ large_url:
+ type: ["string", "null"]
+ file_url:
+ type: ["string", "null"]
+ preview_width:
+ type: number
+ preview_height:
+ type: number
+ DTextResponse:
+ type: object
+ required:
+ - html
+ - posts
+ properties:
+ html:
+ type: string
+ posts:
+ type: object
+ patternProperties:
+ "^0-9+$":
+ $ref: "#/components/schemas/DeferredPost"
+ EmailBlacklist:
+ type: object
+ required:
+ - id
+ - created_at
+ - updated_at
+ - domain
+ - creator_id
+ - reason
+ properties:
+ id:
+ type: number
+ created_at:
+ type: string
+ updated_at:
+ type: string
+ domain:
+ type: string
+ creator_id:
+ type: string
+ reason:
+ type: string
+ ForumPost:
+ type: object
+ required:
+ - id
+ - topic_id
+ - creator_id
+ - updater_id
+ - body
+ - is_hidden
+ - created_at
+ - updated_at
+ - warning_type
+ - warning_user_id
+ properties:
+ id:
+ type: number
+ topic:
+ type: number
+ creator_id:
+ type: number
+ updater_id:
+ type: number
+ body:
+ type: string
+ is_hidden:
+ type: boolean
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ warning_type:
+ type: ["string", "null"]
+ enum: *warning-types
+ warning_user_id:
+ type: ["number", "null"]
+ ForumPostVote:
+ type: object
+ required:
+ - id
+ - forum_post_id
+ - creator_id
+ - score
+ - created_at
+ - updated_at
+ - creator_name
+ properties:
+ id:
+ type: number
+ forum_post_id:
+ type: number
+ creator_id:
+ type: number
+ score:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ creator_name:
+ type: string
+ ForumTopic:
+ type: object
+ required:
+ - id
+ - creator_id
+ - updater_id
+ - title
+ - response_count
+ - is_sticky
+ - is_locked
+ - is_hidden
+ - created_at
+ - updated_at
+ - category_id
+ properties:
+ id:
+ type: number
+ creator_id:
+ type: number
+ updater_id:
+ type: number
+ title:
+ type: string
+ response_count:
+ type: number
+ is_sticky:
+ type: boolean
+ is_locked:
+ type: boolean
+ is_hidden:
+ type: boolean
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ category_id:
+ type: number
+ FullCurrentUser:
+ type: object
+ allOf:
+ - $ref: "#/components/schemas/CurrentUser"
+ - $ref: "#/components/schemas/FullUser"
+ FullUser:
+ allOf:
+ - $ref: "#/components/schemas/User"
+ - type: object
+ required:
+ - wiki_page_version_count
+ - artist_version_count
+ - pool_version_count
+ - forum_post_count
+ - comment_count
+ - flag_count
+ - favorite_count
+ - positive_feedback_count
+ - neutral_feedback_count
+ - negative_feedback_count
+ - upload_limit
+ - profile_about
+ - profile_artinfo
+ properties:
+ wiki_page_version_count:
+ type: number
+ artist_version_count:
+ type: number
+ pool_version_count:
+ type: number
+ forum_post_count:
+ type: number
+ comment_count:
+ type: number
+ flag_count:
+ type: number
+ favorite_count:
+ type: number
+ positive_feedback_count:
+ type: number
+ neutral_feedback_count:
+ type: number
+ negative_feedback_count:
+ type: number
+ upload_limit:
+ type: number
+ profile_about:
+ type: string
+ profile_artinfo:
+ type: string
+ Help:
+ type: object
+ required:
+ - id
+ - name
+ - title
+ - wiki_page
+ - related
+ - created_at
+ - updated_at
+ properties:
+ id:
+ type: number
+ name:
+ type: string
+ title:
+ type: string
+ wiki_page:
+ type: string
+ related:
+ type: string
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ IPBan:
+ type: object
+ description: Due to a global filter, the ip_addr is not present no matter your user level.
+ required:
+ - id
+ - creator_id
+ - reason
+ - created_at
+ - updated_at
+ properties:
+ id:
+ type: number
+ creator_id:
+ type: number
+ reason:
+ type: string
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ IQDBPost:
+ type: object
+ required:
+ - id
+ - created_at
+ - updated_at
+ - up_score
+ - down_score
+ - score
+ - source
+ - md5
+ - rating
+ - is_note_locked
+ - is_rating_locked
+ - is_status_locked
+ - is_pending
+ - is_flagged
+ - is_deleted
+ - uploader_id
+ - approver_id
+ - last_noted_at
+ - last_comment_bumped_at
+ - fav_count
+ - tag_string
+ - tag_count
+ - tag_count_general
+ - tag_count_artist
+ - tag_count_character
+ - tag_count_copyright
+ - file_ext
+ - file_size
+ - image_width
+ - image_height
+ - parent_id
+ - has_children
+ - last_commented_at
+ - has_active_children
+ - bit_flags
+ - tag_count_meta
+ - locked_tags
+ - tag_count_species
+ - tag_count_invalid
+ - description
+ - comment_count
+ - change_seq
+ - tag_count_lore
+ - bg_color
+ - generated_samples
+ - duration
+ - is_comment_disabled
+ - is_comment_locked
+ - has_large
+ - has_visible_children
+ - children_ids
+ - pool_ids
+ - is_favorited
+ properties:
+ id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ up_score:
+ type: number
+ down_score:
+ type: number
+ score:
+ type: number
+ source:
+ type: string
+ md5:
+ type: string
+ rating:
+ type: string
+ enum: *ratings
+ is_note_locked:
+ type: boolean
+ is_rating_locked:
+ type: boolean
+ is_status_locked:
+ type: boolean
+ is_pending:
+ type: boolean
+ is_flagged:
+ type: boolean
+ is_deleted:
+ type: boolean
+ uploader_id:
+ type: number
+ approver_id:
+ type: number
+ last_noted_at:
+ type: ["null", "string"]
+ format: date-time
+ last_comment_bumped_at:
+ type: ["null", "string"]
+ format: date-time
+ fav_count:
+ type: number
+ tag_string:
+ type: string
+ tag_count:
+ type: number
+ tag_count_general:
+ type: number
+ tag_count_artist:
+ type: number
+ tag_count_character:
+ type: number
+ tag_count_copyright:
+ type: number
+ file_ext:
+ type: string
+ file_size:
+ type: number
+ image_width:
+ type: number
+ image_height:
+ type: number
+ parent_id:
+ type: ["null", "number"]
+ has_children:
+ type: boolean
+ last_commented_at:
+ type: ["null", "string"]
+ format: date-time
+ has_active_children:
+ type: boolean
+ bit_flags:
+ type: number
+ tag_count_meta:
+ type: number
+ locked_tags:
+ type: ["null", "string"]
+ tag_count_species:
+ type: number
+ tag_count_invalid:
+ type: number
+ description:
+ type: string
+ comment_count:
+ type: number
+ change_seq:
+ type: number
+ tag_count_lore:
+ type: number
+ bg_color:
+ type: ["null", "string"]
+ generated_samples:
+ type: ["null", "array"]
+ items:
+ type: string
+ enum:
+ - 720p
+ - 480p
+ - original
+ duration:
+ type: ["null", "string"]
+ is_comment_disabled:
+ type: boolean
+ is_comment_locked:
+ type: boolean
+ has_large:
+ type: boolean
+ has_visible_children:
+ type: boolean
+ children_ids:
+ type: ["null", "string"]
+ pool_ids:
+ type: array
+ items:
+ type: number
+ is_favorited:
+ type: boolean
+ file_url:
+ type: string
+ large_file_url:
+ type: string
+ preview_file_url:
+ type: string
+ IQDBResponse:
+ type: object
+ required:
+ - hash
+ - post_id
+ - score
+ - post
+ properties:
+ hash:
+ type: string
+ post_id:
+ type: number
+ score:
+ type: number
+ post:
+ type: object
+ required:
+ - posts
+ properties:
+ posts:
+ $ref: "#/components/schemas/IQDBPost"
+ Mascot:
+ type: object
+ required:
+ - id
+ - creator_id
+ - display_name
+ - md5
+ - file_ext
+ - background_color
+ - artist_url
+ - artist_name
+ - active
+ - created_at
+ - updated_at
+ - available_on
+ - url_path
+ properties:
+ id:
+ type: number
+ creator_id:
+ type: number
+ display_name:
+ type: string
+ md5:
+ type: string
+ file_ext:
+ type: string
+ background_color:
+ type: string
+ artist_url:
+ type: string
+ format: uri
+ artist_name:
+ type: string
+ active:
+ type: boolean
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ available_on:
+ type: array
+ items:
+ type: string
+ url_path:
+ type: string
+ format: uri
+ ModAction:
+ type: object
+ required:
+ - id
+ - creator_id
+ - created_at
+ - updated_at
+ - action
+ properties:
+ id:
+ type: number
+ creator_id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ action:
+ type: string
+ enum: *modaction-actions
+ NewsUpdate:
+ type: object
+ required:
+ - id
+ - message
+ - creator_id
+ - updater_id
+ - created_at
+ - updated_at
+ properties:
+ id:
+ type: number
+ message:
+ type: string
+ creator_id:
+ type: number
+ updater_id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ Note:
+ type: object
+ required:
+ - id
+ - created_at
+ - updated_at
+ - creator_id
+ - x
+ - y
+ - width
+ - height
+ - version
+ - is_active
+ - post_id
+ - body
+ - creator_name
+ properties:
+ id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ creator_id:
+ type: number
+ x:
+ type: number
+ y:
+ type: number
+ width:
+ type: number
+ height:
+ type: number
+ version:
+ type: number
+ is_active:
+ type: boolean
+ post_id:
+ type: number
+ body:
+ type: string
+ creator_name:
+ type: string
+ NoteVersion:
+ type: object
+ required:
+ - id
+ - created_at
+ - updated_at
+ - x
+ - y
+ - width
+ - height
+ - body
+ - version
+ - is_active
+ - note_id
+ - post_id
+ - updater_id
+ properties:
+ id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ x:
+ type: number
+ y:
+ type: number
+ width:
+ type: number
+ height:
+ type: number
+ body:
+ type: string
+ version:
+ type: number
+ is_active:
+ type: boolean
+ note_id:
+ type: number
+ post_id:
+ type: number
+ updater_id:
+ type: number
+ Pool:
+ type: object
+ required:
+ - id
+ - name
+ - created_at
+ - updated_at
+ - creator_id
+ - description
+ - is_active
+ - category
+ - post_ids
+ - creator_name
+ - post_count
+ properties:
+ id:
+ type: number
+ name:
+ type: string
+ updated_at:
+ type: string
+ format: date-time
+ creator_id:
+ type: number
+ description:
+ type: string
+ is_active:
+ type: boolean
+ category:
+ type: string
+ enum: *pool-categories
+ post_ids:
+ type: array
+ items:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ creator_name:
+ type: string
+ post_count:
+ type: number
+ PoolVersion:
+ type: object
+ required:
+ - id
+ - pool_id
+ - post_ids
+ - updater_id
+ - created_at
+ - updated_at
+ - name
+ - name_changed
+ - description
+ - description_changed
+ - is_active
+ - is_locked
+ - category
+ - version
+ - added_post_ids
+ - removed_post_ids
+ properties:
+ id:
+ type: number
+ pool_id:
+ type: number
+ post_ids:
+ type: array
+ items:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ updater_id:
+ type: number
+ name:
+ type: string
+ name_changed:
+ type: boolean
+ description:
+ type: string
+ description_changed:
+ type: boolean
+ is_active:
+ type: boolean
+ is_locked:
+ type: boolean
+ category:
+ type: string
+ enum: *pool-categories
+ version:
+ type: number
+ added_post_ids:
+ type: array
+ items:
+ type: number
+ removed_post_ids:
+ type: array
+ items:
+ type: number
+ Post:
+ type: object
+ required:
+ - id
+ - created_at
+ - updated_at
+ - file
+ - preview
+ - sample
+ - score
+ - tags
+ - locked_tags
+ - change_seq
+ - flags
+ - rating
+ - fav_count
+ - sources
+ - pools
+ - relationships
+ - approver_id
+ - uploader_id
+ - description
+ - comment_count
+ - is_favorited
+ - has_notes
+ - duration
+ properties:
+ id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ file:
+ type: object
+ required:
+ - width
+ - height
+ - ext
+ - size
+ - md5
+ - url
+ properties:
+ width:
+ type: number
+ height:
+ type: number
+ ext:
+ type: string
+ size:
+ type: number
+ md5:
+ type: string
+ url:
+ type: ["string", "null"]
+ preview:
+ type: object
+ required:
+ - width
+ - height
+ - url
+ properties:
+ width:
+ type: number
+ height:
+ type: number
+ url:
+ type: ["string", "null"]
+ sample:
+ type: object
+ required:
+ - has
+ - height
+ - width
+ - url
+ - alternates
+ properties:
+ has:
+ type: boolean
+ height:
+ type: ["number", "null"]
+ width:
+ type: ["number", "null"]
+ url:
+ type: ["string", "null"]
+ alternates:
+ type: object
+ properties:
+ 480p:
+ $ref: "#/components/schemas/PostSampleAlternate"
+ 720p:
+ $ref: "#/components/schemas/PostSampleAlternate"
+ original:
+ $ref: "#/components/schemas/PostSampleAlternate"
+ score:
+ type: object
+ required:
+ - up
+ - down
+ - total
+ properties:
+ up:
+ type: number
+ down:
+ type: number
+ total:
+ type: number
+ tags:
+ type: object
+ required:
+ - general
+ - artist
+ - copyright
+ - character
+ - species
+ - invalid
+ - meta
+ - lore
+ properties:
+ general:
+ type: array
+ items:
+ type: string
+ artist:
+ type: array
+ items:
+ type: string
+ copyright:
+ type: array
+ items:
+ type: string
+ character:
+ type: array
+ items:
+ type: string
+ species:
+ type: array
+ items:
+ type: string
+ invalid:
+ type: array
+ items:
+ type: string
+ meta:
+ type: array
+ items:
+ type: string
+ lore:
+ type: array
+ items:
+ type: string
+ locked_tags:
+ type: ["array", "null"]
+ items:
+ type: string
+ change_seq:
+ type: number
+ flags:
+ type: object
+ required:
+ - pending
+ - flagged
+ - note_locked
+ - status_locked
+ - rating_locked
+ - deleted
+ properties:
+ pending:
+ type: boolean
+ flagged:
+ type: boolean
+ note_locked:
+ type: boolean
+ status_locked:
+ type: boolean
+ rating_locked:
+ type: boolean
+ deleted:
+ type: boolean
+ rating:
+ type: string
+ enum: *ratings
+ fav_count:
+ type: number
+ sources:
+ type: array
+ items:
+ type: string
+ pools:
+ type: array
+ items:
+ type: number
+ relationships:
+ type: object
+ required:
+ - parent_id
+ - has_children
+ - has_active_children
+ - children
+ properties:
+ parent_id:
+ type: ["number", "null"]
+ has_children:
+ type: boolean
+ has_active_children:
+ type: boolean
+ children:
+ type: array
+ items:
+ type: number
+ approver_id:
+ type: ["number", "null"]
+ uploader_id:
+ type: number
+ description:
+ type: string
+ comment_count:
+ type: number
+ is_favorited:
+ type: boolean
+ has_notes:
+ type: boolean
+ duration:
+ type: ["number", "null"]
+ PostApproval:
+ type: object
+ required:
+ - id
+ - user_id
+ - post_id
+ - created_at
+ - updated_at
+ properties:
+ id:
+ type: number
+ user_id:
+ type: number
+ post_id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ PostDisapproval:
+ type: object
+ required:
+ - id
+ - user_id
+ - post_id
+ - reason
+ - message
+ - created_at
+ - updated_at
+ properties:
+ id:
+ type: number
+ user_id:
+ type: number
+ post_id:
+ type: number
+ reason:
+ type: string
+ enum:
+ - borderline_quality
+ - borderline_relevancy
+ - other
+ message:
+ type: ["string", "null"]
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ PostEvent:
+ type: object
+ required:
+ - id
+ - creator_id
+ - post_id
+ - action
+ - created_at
+ properties:
+ id:
+ type: number
+ creator_id:
+ type: ["number", "null"]
+ post_id:
+ type: number
+ action:
+ type: string
+ enum: *post-event-actions
+ created_at:
+ type: string
+ format: date-time
+ PostFlag:
+ type: object
+ required:
+ - id
+ - created_at
+ - post_id
+ - reason
+ - creator_id
+ - is_resolved
+ - updated_at
+ - is_deletion
+ - type
+ properties:
+ id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ post_id:
+ type: number
+ reason:
+ type: string
+ creator_id:
+ type: ["number", "null"]
+ is_resolved:
+ type: boolean
+ updated_at:
+ type: string
+ format: date-time
+ is_deletion:
+ type: boolean
+ type:
+ type: string
+ enum:
+ - flag
+ - deletion
+ PostReplacement:
+ type: object
+ required:
+ - id
+ - created_at
+ - updated_at
+ - post_id
+ - creator_id
+ - approver_id
+ - file_ext
+ - file_size
+ - image_height
+ - image_width
+ - md5
+ - source
+ - file_name
+ - status
+ - reason
+ properties:
+ id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ post_id:
+ type: number
+ creator_id:
+ type: number
+ approver_id:
+ type: ["number", "null"]
+ file_ext:
+ type: string
+ file_size:
+ type: number
+ image_height:
+ type: number
+ image_width:
+ type: number
+ md5:
+ type: string
+ source:
+ type: string
+ file_name:
+ type: string
+ status:
+ type: string
+ enum:
+ - prompted
+ - approved
+ - rejected
+ - pending
+ reason:
+ type: string
+ PostSampleAlternate:
+ type: object
+ required:
+ - type
+ - height
+ - width
+ - urls
+ properties:
+ type:
+ type: string
+ enum:
+ - video
+ height:
+ type: number
+ width:
+ type: number
+ urls:
+ type: array
+ items:
+ anyOf:
+ - type: ["string", "null"]
+ format: "uri"
+ - type: ["string", "null"]
+ format: "uri"
+ PostSet:
+ type: object
+ required:
+ - id
+ - created_at
+ - updated_at
+ - creator_id
+ - is_public
+ - name
+ - shortname
+ - description
+ - transfer_on_delete
+ - post_ids
+ - post_count
+ properties:
+ id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ creator_id:
+ type: number
+ is_public:
+ type: boolean
+ name:
+ type: string
+ shortname:
+ type: string
+ description:
+ type: string
+ post_count:
+ type: number
+ transfer_on_delete:
+ type: boolean
+ post_ids:
+ type: array
+ items:
+ type: number
+ PostVersion:
+ type: object
+ required:
+ - id
+ - post_id
+ - tags
+ - updater_id
+ - updated_at
+ - rating
+ - parent_id
+ - source
+ - description
+ - reason
+ - locked_tags
+ - added_tags
+ - removed_tags
+ - added_locked_tags
+ - removed_locked_tags
+ - rating_changed
+ - parent_changed
+ - source_changed
+ - description_changed
+ - version
+ - obsolete_added_tags
+ - obsolete_removed_tags
+ - unchanged_tags
+ - updater_name
+ properties:
+ id:
+ type: number
+ post_id:
+ type: number
+ tags:
+ type: string
+ updater_id:
+ type: number
+ updated_at:
+ type: string
+ format: date-time
+ rating:
+ type: string
+ enum: *ratings
+ parent_id:
+ type: ["number", "null"]
+ source:
+ type: string
+ description:
+ type: string
+ reason:
+ type: ["string", "null"]
+ locked_tags:
+ type: ["string", "null"]
+ added_tags:
+ type: array
+ items:
+ type: string
+ removed_tags:
+ type: array
+ items:
+ type: string
+ added_locked_tags:
+ type: array
+ items:
+ type: string
+ removed_locked_tags:
+ type: array
+ items:
+ type: string
+ rating_changed:
+ type: boolean
+ parent_changed:
+ type: boolean
+ source_changed:
+ type: boolean
+ description_changed:
+ type: boolean
+ version:
+ type: number
+ obsolete_added_tags:
+ type: string
+ obsolete_removed_tags:
+ type: string
+ unchanged_tags:
+ type: string
+ updater_name:
+ type: string
+ RelatedTag:
+ type: object
+ required:
+ - name
+ - category_id
+ properties:
+ name:
+ type: string
+ category_id:
+ type: number
+ enum: *tag-categories
+ Tag:
+ type: object
+ required:
+ - id
+ - name
+ - post_count
+ - related_tags
+ - related_tags_updated_at
+ - category
+ - is_locked
+ - created_at
+ - updated_at
+ properties:
+ id:
+ type: number
+ name:
+ type: string
+ post_count:
+ type: number
+ related_tags:
+ type: array
+ items:
+ type: string
+ related_tags_updated_at:
+ type: ["string", "null"]
+ format: date-time
+ category:
+ type: number
+ enum: *tag-categories
+ is_locked:
+ type: boolean
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ TagAlias:
+ type: object
+ required:
+ - id
+ - antecedent_name
+ - reason
+ - creator_id
+ - created_at
+ - forum_post_id
+ - updated_at
+ - forum_topic_id
+ - consequent_name
+ - status
+ - post_count
+ - approver_id
+ properties:
+ id:
+ type: number
+ antecedent_name:
+ type: string
+ reason:
+ type: string
+ creator_id:
+ type: number
+ created_at:
+ type: ["string", "null"]
+ format: date-time
+ forum_post_id:
+ type: ["number", "null"]
+ updated_at:
+ type: ["string", "null"]
+ format: date-time
+ forum_topic_id:
+ type: ["number", "null"]
+ consequent_name:
+ type: string
+ status:
+ type: string
+ description: |
+ Note: The "error" status will be proceeded by an error, ex: "error: Validation failed: A tag alias for tag_name already exists"
+ enum: *tag-request-statuses
+ post_count:
+ type: number
+ approver_id:
+ type: ["number", "null"]
+ TagImplication:
+ type: object
+ required:
+ - id
+ - reason
+ - creator_id
+ - created_at
+ - forum_post_id
+ - antecedent_name
+ - consequent_name
+ - status
+ - forum_topic_id
+ - updated_at
+ - descendant_names
+ - approver_id
+ properties:
+ id:
+ type: number
+ reason:
+ type: string
+ creator_id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ forum_post_id:
+ type: ["number", "null"]
+ antecedent_name:
+ type: string
+ consequent_name:
+ type: string
+ status:
+ type: string
+ description: |
+ Note: The "error" status will be proceeded by an error, ex: "error: Validation failed: A tag alias for tag_name already exists"
+ enum: *tag-request-statuses
+ forum_topic_id:
+ type: ["number", "null"]
+ updated_at:
+ type: string
+ format: date-time
+ descendant_names:
+ type: array
+ items:
+ type: string
+ approver_id:
+ type: ["number", "null"]
+ TagPreview:
+ type: object
+ required:
+ - a
+ - type
+ - tagTypeA
+ properties:
+ a:
+ type: string
+ description: The name if type=tag, else the antecedent.
+ b:
+ type: string
+ description: The consequent, only present if type=alias or type=implication.
+ type:
+ type: string
+ enum:
+ - tag
+ - implication
+ - alias
+ tagTypeA:
+ type: ["number", "null"]
+ enum: *tag-categories
+ tagTypeB:
+ type: ["number", "null"]
+ enum: *tag-categories
+ TagTypeVersion:
+ type: object
+ required:
+ - id
+ - created_at
+ - updated_at
+ - old_type
+ - new_type
+ - is_locked
+ - tag_id
+ - creator_id
+ properties:
+ id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ old_type:
+ type: number
+ enum: *tag-categories
+ new_type:
+ type: number
+ enum: *tag-categories
+ is_locked:
+ type: boolean
+ tag_id:
+ type: number
+ creator_id:
+ type: number
+ Takedown:
+ type: object
+ required:
+ - id
+ - status
+ - approver_id
+ - reason_hidden
+ - created_at
+ - updated_at
+ - post_count
+ properties:
+ id:
+ type: number
+ status:
+ type: string
+ enum:
+ - approved
+ - denied
+ - partial
+ - pending
+ approver_id:
+ type: ["number", "null"]
+ reason_hidden:
+ type: boolean
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ post_count:
+ type: number
+ Ticket:
+ type: object
+ required:
+ - id
+ - creator_id
+ - reason
+ - disp_id
+ - qtype
+ - status
+ - created_at
+ - updated_at
+ - response
+ - handler_id
+ - report_reason
+ - accused_id
+ properties:
+ id:
+ type: number
+ creator_id:
+ type: number
+ reason:
+ type: string
+ disp_id:
+ type: number
+ qtype:
+ type: string
+ enum: *ticket-types
+ status:
+ type: string
+ enum: *ticket-statuses
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ response:
+ type: string
+ handler_id:
+ type: ["number", "null"]
+ claimant_id:
+ type: ["number", "null"]
+ description: Only visible to Moderator+.
+ report_reason:
+ type: ["string", "null"]
+ accused_id:
+ type: ["number", "null"]
+ UploadWhitelist:
+ type: object
+ required:
+ - id
+ - created_at
+ - updated_at
+ - pattern
+ - note
+ - hidden
+ - allowed
+ - reason
+ properties:
+ id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ pattern:
+ type: string
+ note:
+ type: string
+ hidden:
+ type: boolean
+ allowed:
+ type: boolean
+ reason:
+ type: string
+ Upload:
+ type: object
+ required:
+ - id
+ - source
+ - rating
+ - uploader_id
+ - tag_string
+ - status
+ - backtrace
+ - post_id
+ - md5_confirmation
+ - created_at
+ - updated_at
+ - parent_id
+ - md5
+ - file_ext
+ - file_size
+ - image_width
+ - image_height
+ - description
+ - uploader_name
+ properties:
+ id:
+ type: number
+ source:
+ type: string
+ rating:
+ type: string
+ enum: *ratings
+ uploader_id:
+ type: number
+ tag_string:
+ type: string
+ status:
+ type: string
+ description: |
+ Note: The "error" status will be proceeded by an error, ex: "error: RuntimeError - No file or source URL provided"
+ enum:
+ - completed
+ - duplicate
+ - error
+ - processing
+ - pending
+ backtrace:
+ type: ["string", "null"]
+ post_id:
+ type: ["number", "null"]
+ md5_confirmation:
+ type: "null"
+ deprecated: true
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ parent_id:
+ type: ["null", "number"]
+ md5:
+ type: ["string", "null"]
+ file_ext:
+ type: ["string", "number"]
+ file_size:
+ type: ["null", "number"]
+ image_width:
+ type: ["null", "number"]
+ image_height:
+ type: ["null", "number"]
+ description:
+ type: string
+ uploader_name:
+ type: string
+ User:
+ type: object
+ required:
+ - id
+ - created_at
+ - name
+ - level
+ - base_upload_limit
+ - post_upload_count
+ - post_update_count
+ - note_update_count
+ - is_banned
+ - can_approve_posts
+ - can_upload_free
+ - level_string
+ - avatar_id
+ properties:
+ id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ name:
+ type: string
+ level:
+ type: number
+ base_upload_limit:
+ type: number
+ post_upload_count:
+ type: number
+ post_update_count:
+ type: number
+ note_update_count:
+ type: number
+ is_banned:
+ type: boolean
+ can_approve_posts:
+ type: boolean
+ can_upload_free:
+ type: boolean
+ level_string:
+ type: string
+ avatar_id:
+ type: ["number", "null"]
+ UserFeedback:
+ type: object
+ required:
+ - id
+ - user_id
+ - creator_id
+ - category
+ - body
+ - created_at
+ - updated_at
+ - updater_id
+ - is_deleted
+ properties:
+ id:
+ type: number
+ user_id:
+ type: number
+ creator_id:
+ type: number
+ category:
+ type: string
+ enum: *feedback-categories
+ body:
+ type: string
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ updater_id:
+ type: number
+ is_deleted:
+ type: boolean
+ UserNameChangeRequest:
+ type: object
+ required:
+ - id
+ - approver_id
+ - user_id
+ - original_name
+ - desired_name
+ - created_at
+ - updated_at
+ - status
+ properties:
+ id:
+ type: number
+ approver_id:
+ type: number
+ user_id:
+ type: number
+ original_name:
+ type: string
+ desired_name:
+ type: string
+ change_reason:
+ type: string
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ status:
+ type: string
+ enum:
+ - approved
+ WikiPage:
+ type: object
+ required:
+ - id
+ - created_at
+ - updated_at
+ - title
+ - body
+ - creator_id
+ - is_locked
+ - updater_id
+ - is_deleted
+ - other_names
+ - parent
+ - creator_name
+ - category_id
+ properties:
+ id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ title:
+ type: string
+ body:
+ type: string
+ creator_id:
+ type: number
+ is_locked:
+ type: boolean
+ updater_id:
+ type: number
+ is_deleted:
+ type: boolean
+ other_names:
+ type: array
+ items:
+ type: string
+ parent:
+ type: ["string", "null"]
+ creator_name:
+ type: string
+ category_id:
+ type: number
+ enum: *tag-categories
+ WikiPageVersion:
+ type: object
+ required:
+ - id
+ - created_at
+ - updated_at
+ - title
+ - body
+ - updater_id
+ - wiki_page_id
+ - is_locked
+ - other_names
+ - is_deleted
+ - reason
+ - parent
+ properties:
+ id:
+ type: number
+ created_at:
+ type: string
+ format: date-time
+ updated_at:
+ type: string
+ format: date-time
+ title:
+ type: string
+ body:
+ type: string
+ updater_id:
+ type: number
+ wiki_page_id:
+ type: number
+ is_locked:
+ type: boolean
+ other_names:
+ type: array
+ items:
+ type: string
+ is_deleted:
+ type: boolean
+ reason:
+ type: ["string", "null"]
+ parent:
+ type: ["string", "null"]
+ responses:
+ NotFound:
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - success
+ - reason
+ properties:
+ success:
+ type: boolean
+ enum:
+ - false
+ reason:
+ type: string
+ enum:
+ - "not found"
+ AccessDenied:
+ description: Access Denied
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - success
+ - reason
+ properties:
+ success:
+ type: boolean
+ enum:
+ - false
+ reason:
+ type: string
+ enum:
+ - "Access Denied"
+ ExpectedError:
+ description: Invalid Input Data
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - errors
+ examples:
+ - { "errors": { "key": [ "the error" ] } }
+ properties:
+ errors:
+ type: object
+ patternProperties:
+ "^[a-z]+$":
+ type: array
+ items:
+ type: string
+ MessageError:
+ description: Error
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - success
+ - message
+ properties:
+ success:
+ type: boolean
+ enum:
+ - false
+ message:
+ type: string
+ code:
+ type: ["string", "null"]
+ requestBodies:
+ warning:
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - record_type
+ properties:
+ record_type:
+ type: string
+ enum:
+ - unmark
+ - ban
+ - record
+ - warning
+
+paths:
+ # Artists
+ /artists.json:
+ get:
+ summary: Search Artists
+ operationId: searchArtists
+ tags:
+ - Artists
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - updated_at
+ - name
+ - post_count
+ - name: search[name]
+ in: query
+ schema:
+ type: string
+ - name: search[group_name]
+ in: query
+ schema:
+ type: string
+ - name: search[any_other_name_like]
+ in: query
+ schema:
+ type: string
+ - name: search[any_name_matches]
+ in: query
+ schema:
+ type: string
+ - name: search[any_name_or_url_matches]
+ in: query
+ schema:
+ type: string
+ - name: search[url_matches]
+ in: query
+ schema:
+ type: string
+ - name: search[creator_name]
+ in: query
+ schema:
+ type: string
+ - name: search[creator_id]
+ in: query
+ schema:
+ type: string
+ - name: search[has_tag]
+ in: query
+ schema:
+ type: string
+ - name: search[is_linked]
+ in: query
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ allOf:
+ - $ref: "#/components/schemas/Artist"
+ - type: object
+ required:
+ - domains
+ - urls
+ properties:
+ domains:
+ type: array
+ example: [["e621.net", 1]]
+ items:
+ type: array
+ items:
+ anyOf:
+ - type: string
+ - type: number
+ urls:
+ type: array
+ items:
+ $ref: "#/components/schemas/ArtistURL"
+ post:
+ summary: Create Artist
+ operationId: createArtist
+ tags:
+ - Artists
+ security:
+ - basicAuth: []
+ description: |
+ `other_names` & `urls` are silently truncated to 25 entries.
+ `notes` is silently truncated to the wiki page limit (250,000).
+ Individual `other_names` are silently truncated to 100 characters.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - artist[name]
+ properties:
+ artist[name]:
+ type: string
+ artist[other_names]:
+ type: array
+ items:
+ type: string
+ artist[other_names_string]:
+ type: string
+ artist[url_string]:
+ type: string
+ artist[notes]:
+ type: string
+ artist[group_name]:
+ type: string
+ artist[linked_user_id]:
+ type: ["number", "null"]
+ description: Only usable for Janitor+
+ artist[is_locked]:
+ type: boolean
+ description: Only usable for Janitor+
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Artist"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /artists/{idOrName}.json:
+ get:
+ summary: Get Artist
+ operationId: getArtist
+ tags:
+ - Artists
+ parameters:
+ - name: idOrName
+ in: path
+ required: true
+ description: The ID or name of the artist to get.
+ schema:
+ type: ["number", "string"]
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ allOf:
+ - $ref: "#/components/schemas/Artist"
+ - type: object
+ required:
+ - domains
+ - urls
+ properties:
+ domains:
+ type: array
+ items:
+ type: array
+ example: [["e621.net", 1]]
+ items:
+ anyOf:
+ - type: string
+ - type: number
+ urls:
+ type: array
+ items:
+ $ref: "#/components/schemas/ArtistURL"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Artist
+ operationId: editArtist
+ tags:
+ - Artists
+ security:
+ - basicAuth: []
+ description: |
+ If an artist is locked, you must be Janitor+ to edit them.
+ `other_names` & `urls` are silently truncated to 25 entries.
+ `notes` is silently truncated to the wiki page limit (250,000).
+ Individual `other_names` are silently truncated to 100 characters.
+ If an artist is on the avoid posting list, you must have the bd staff user flag to edit name, other_names, or group_name.
+ parameters:
+ - name: idOrName
+ in: path
+ required: true
+ description: The ID or name of the artist to edit.
+ schema:
+ type: ["number", "string"]
+ requestBody:
+ content:
+ application/c-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ artist[name]:
+ type: string
+ description: Only usable for Janitor+
+ artist[other_names]:
+ type: array
+ items:
+ type: string
+ artist[other_names_string]:
+ type: string
+ artist[url_string]:
+ type: string
+ artist[notes]:
+ type: string
+ artist[group_name]:
+ type: string
+ artist[linked_user_id]:
+ type: ["number", "null"]
+ description: Only usable for Janitor+
+ artist[is_locked]:
+ type: boolean
+ description: Only usable for Janitor+
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Artist
+ operationId: deleteArtist
+ tags:
+ - Artists
+ security:
+ - basicAuth: []
+ description: |
+ You must be an Admin+ to delete an artist.
+ parameters:
+ - name: idOrName
+ in: path
+ required: true
+ description: The ID or name of the artist to edit.
+ schema:
+ type: ["number", "string"]
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /artists/{idOrName}/revert.json:
+ put:
+ summary: Revert Artist
+ operationId: revertArtist
+ tags:
+ - Artists
+ security:
+ - basicAuth: []
+ description: |
+ If an artist is locked, you must be Janitor+ to revert them.
+ parameters:
+ - name: idOrName
+ in: path
+ required: true
+ description: The ID or name of the artist to revert.
+ schema:
+ type: ["number", "string"]
+ - name: version_id
+ in: query
+ required: true
+ description: The version ID to revert to.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ # Artist Versions
+ /artist_versions.json:
+ get:
+ summary: Search Artist Versions
+ operationId: searchArtistVersions
+ tags:
+ - Artist Versions
+ description: When no results are found, an object with an `artist_versions` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - $ref: "#/components/parameters/ip_addr"
+ - name: search[order]
+ in: query
+ description: The order of the results.
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - name
+ - name: search[name]
+ in: query
+ description: The name of the artist.
+ schema:
+ type: string
+ - name: search[artist_id]
+ in: query
+ description: The id of the artist.
+ schema:
+ type: string
+ - name: search[updater_name]
+ in: query
+ description: The name of the user that updated the artist.
+ schema:
+ type: string
+ - name: search[updater_id]
+ in: query
+ description: The id of the user that updated the artist.
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/ArtistVersion"
+ - type: object
+ description: No Results
+ required:
+ - artist_versions
+ properties:
+ artist_versions:
+ type: array
+ maxItems: 0
+ # Artist URLs
+ /artist_urls.json:
+ get:
+ summary: Search Artist URLs
+ operationId: searchArtistUrls
+ tags:
+ - Artist URLs
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ description: The order of the results.
+ schema:
+ type: string
+ enum:
+ - id
+ - id_asc
+ - id_desc
+ - artist_id
+ - artist_id_asc
+ - artist_id_desc
+ - url
+ - url_asc
+ - url_desc
+ - normalized_url
+ - normalized_url_asc
+ - normalized_url_desc
+ - created_at
+ - created_at_asc
+ - created_at_desc
+ - updated_at
+ - updated_at_asc
+ - updated_at_desc
+ - name: search[artist_name]
+ in: query
+ description: The name of the artist.
+ schema:
+ type: string
+ - name: search[artist_id]
+ in: query
+ description: The id of the artist.
+ schema:
+ type: string
+ - name: search[is_active]
+ in: query
+ description: If the artist url is active.
+ schema:
+ type: boolean
+ - name: search[url]
+ in: query
+ description: The url.
+ schema:
+ type: string
+ - name: search[normalized_url]
+ in: query
+ description: The normalized url. (http:, trailing `/`)
+ schema:
+ type: string
+ - name: search[artist]
+ in: query
+ description: Legacy nested search for artist. Supports the same parameters as /artists.json.
+ deprecated: true
+ schema:
+ type: object
+ - name: search[url_matches]
+ in: query
+ description: Legacy name for `search[url]`.
+ deprecated: true
+ schema:
+ type: string
+ - name: search[normalized_url_matches]
+ in: query
+ description: Legacy name for `search[normalized_url]`.
+ deprecated: true
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ allOf:
+ - $ref: "#/components/schemas/ArtistURL"
+ - type: object
+ required:
+ - artist
+ properties:
+ artist:
+ $ref: "#/components/schemas/Artist"
+ # Avoid Posting Entries
+ /avoid_postings.json:
+ get:
+ summary: Search Avoid Posting Entries
+ operationId: searchAvoidPostings
+ tags:
+ - Avoid Posting Entries
+ description: When no results are found, an object with an `avoid_postings` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - $ref: "#/components/parameters/ip_addr"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - name: search[creator_name]
+ in: query
+ schema:
+ type: string
+ - name: search[creator_id]
+ in: query
+ schema:
+ type: string
+ - name: search[any_name_matches]
+ in: query
+ description: Any name matching.
+ schema:
+ type: string
+ - name: search[artist_name]
+ in: query
+ description: The artist name of the avoid posting entry.
+ schema:
+ type: string
+ - name: search[artist_id]
+ in: query
+ description: The artist id for the avoid posting entry.
+ schema:
+ type: string
+ - name: search[any_other_name_matches]
+ in: query
+ description: Any other name matching.
+ schema:
+ type: string
+ - name: search[details]
+ in: query
+ description: The details of the avoid posting entry.
+ schema:
+ type: string
+ - name: search[staff_notes]
+ in: query
+ description: The staff notes on the avoid posting entry. Must be Janitor+ to use.
+ schema:
+ type: string
+ - name: search[is_active]
+ in: query
+ description: If the avoid posting entry is active.
+ schema:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/AvoidPosting"
+ - type: object
+ description: No Results
+ required:
+ - avoid_postings
+ properties:
+ avoid_postings:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create Avoid Posting Entry
+ operationId: createAvoidPosting
+ tags:
+ - Avoid Posting Entries
+ security:
+ - basicAuth: []
+ description: Must have the bd staff user flag. When no results are found, an object with an `avoid_posting_versions` key is returned.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ avoid_posting[details]:
+ type: string
+ avoid_posting[staff_notes]:
+ type: string
+ avoid_posting[is_active]:
+ type: boolean
+ avoid_posting[artist_attributes][id]:
+ type: number
+ avoid_posting[artist_attributes][name]:
+ type: string
+ description: If provided and the artist does not exist, an artist will be created.
+ avoid_posting[artist_attributes][other_names_string]:
+ type: string
+ avoid_posting[artist_attributes][other_names]:
+ type: array
+ items:
+ type: string
+ avoid_posting[artist_attributes][group_name]:
+ type: string
+ avoid_posting[artist_attributes][linked_user_id]:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/AvoidPosting"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /avoid_postings/{idOrArtistName}.json:
+ get:
+ summary: Get Avoid Posting Entry
+ operationId: getAvoidPosting
+ tags:
+ - Avoid Posting Entries
+ parameters:
+ - name: idOrArtistName
+ in: path
+ required: true
+ description: The ID of the avoid posting entry, or the name of the artist.
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/AvoidPosting"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Avoid Posting Entry
+ operationId: editAvoidPosting
+ tags:
+ - Avoid Posting Entries
+ security:
+ - basicAuth: []
+ description: Must have the bd staff user flag.
+ parameters:
+ - name: idOrArtistName
+ in: path
+ required: true
+ description: The ID of the avoid posting entry, or the name of the artist.
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ avoid_posting[details]:
+ type: string
+ avoid_posting[staff_notes]:
+ type: string
+ avoid_posting[is_active]:
+ type: boolean
+ avoid_posting[artist_attributes][name]:
+ type: string
+ avoid_posting[artist_attributes][other_names_string]:
+ type: string
+ avoid_posting[artist_attributes][other_names]:
+ type: array
+ items:
+ type: string
+ avoid_posting[artist_attributes][group_name]:
+ type: string
+ avoid_posting[artist_attributes][linked_user_id]:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/AvoidPosting"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Destroy Avoid Posting Entry
+ operationId: destroyAvoidPosting
+ tags:
+ - Avoid Posting Entries
+ security:
+ - basicAuth: []
+ description: Must have the bd staff user flag.
+ parameters:
+ - name: idOrArtistName
+ in: path
+ required: true
+ description: The ID of the avoid posting entry, or the name of the artist.
+ schema:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /avoid_postings/{idOrArtistName}/delete.json:
+ put:
+ summary: Delete Avoid Posting Entry
+ operationId: deleteAvoidPosting
+ tags:
+ - Avoid Posting Entries
+ security:
+ - basicAuth: []
+ description: Must have the bd staff user flag.
+ parameters:
+ - name: idOrArtistName
+ in: path
+ required: true
+ description: The ID of the avoid posting entry, or the name of the artist.
+ schema:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /avoid_postings/{idOrArtistName}/undelete.json:
+ put:
+ summary: Undelete Avoid Posting Entry
+ operationId: undeleteAvoidPosting
+ tags:
+ - Avoid Posting Entries
+ security:
+ - basicAuth: []
+ description: Must have the bd staff user flag.
+ parameters:
+ - name: idOrArtistName
+ in: path
+ required: true
+ description: The ID of the avoid posting entry, or the name of the artist.
+ schema:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Avoid Posting Versions
+ /avoid_posting_versions.json:
+ get:
+ summary: Search Avoid Posting Versions
+ operationId: searchAvoidPostingVersions
+ tags:
+ - Avoid Posting Versions
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - $ref: "#/components/parameters/ip_addr"
+ - name: search[order]
+ in: query
+ description: The order of the results.
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - name: search[updater_name]
+ in: query
+ description: The name of the updater of the avoid posting entry.
+ schema:
+ type: string
+ - name: search[updater_id]
+ in: query
+ description: The ID of the updater of the avoid posting entry.
+ schema:
+ type: string
+ - name: search[any_name_matches]
+ in: query
+ description: Any name matching.
+ schema:
+ type: string
+ - name: search[artist_name]
+ in: query
+ description: The artist name of the avoid posting entry.
+ schema:
+ type: string
+ - name: search[artist_id]
+ in: query
+ description: The artist id for the avoid posting entry.
+ schema:
+ type: string
+ - name: search[any_other_name_matches]
+ in: query
+ description: Any other name matching.
+ schema:
+ type: string
+ - name: search[group_name]
+ in: query
+ description: Any other name matching.
+ schema:
+ type: string
+ - name: search[is_active]
+ in: query
+ description: If the avoid posting entry is active.
+ schema:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/AvoidPostingVersion"
+ - type: object
+ description: No Results
+ required:
+ - avoid_posting_versions
+ properties:
+ avoid_posting_versions:
+ type: array
+ maxItems: 0
+ # Bans
+ /bans.json:
+ get:
+ summary: Search Bans
+ operationId: searchBans
+ tags:
+ - Bans
+ description: When no results are found, an object with an `bans` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ description: The order of the results.
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - expires_at_desc
+ - name: search[banner_id]
+ in: query
+ description: The ID of the banner.
+ schema:
+ type: string
+ - name: search[banner_name]
+ in: query
+ description: The name of banner.
+ schema:
+ type: string
+ - name: search[user_id]
+ in: query
+ description: The ID of the banned user.
+ schema:
+ type: string
+ - name: search[user_name]
+ in: query
+ description: The name of the banned user.
+ schema:
+ type: string
+ - name: search[reason_matches]
+ in: query
+ description: The reason of the ban.
+ schema:
+ type: string
+ - name: search[expired]
+ in: query
+ description: If the ban is expired.
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/Ban"
+ - type: object
+ description: No Results
+ required:
+ - bans
+ properties:
+ bans:
+ type: array
+ maxItems: 0
+ /bans/{id}.json:
+ get:
+ summary: Get Ban
+ operationId: getBan
+ tags:
+ - Bans
+ parameters:
+ - name: id
+ in: path
+ description: The ID of the ban to get.
+ required: true
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Ban"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Blips
+ /blips.json:
+ get:
+ summary: Search Blips
+ operationId: searchBlips
+ tags:
+ - Blips
+ description: When no results are found, an object with an `blips` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - $ref: "#/components/parameters/ip_addr"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - updated_at
+ - updated_at_desc
+ - name: search[creator_id]
+ in: query
+ schema:
+ type: number
+ - name: search[creator_name]
+ in: query
+ schema:
+ type: string
+ - name: search[body_matches]
+ in: query
+ schema:
+ type: string
+ - name: search[response_to]
+ in: query
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/Blip"
+ - type: object
+ description: No Results
+ required:
+ - blips
+ properties:
+ blips:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create Blip
+ operationId: createBlip
+ tags:
+ - Blips
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - blip[body]
+ properties:
+ blip[body]:
+ type: string
+ blip[response_to]:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Blip"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /blips/{id}.json:
+ get:
+ summary: Get Blip
+ operationId: getBlip
+ tags:
+ - Blips
+ parameters:
+ - name: id
+ in: path
+ description: The ID of the blip to get.
+ required: true
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/Blip"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Blip
+ operationId: editBlip
+ tags:
+ - Blips
+ security:
+ - basicAuth: []
+ description: Unless Admin+, blips cannot be edited after 5 minutes. Marked blips cannot be edited.
+ parameters:
+ - name: id
+ in: path
+ description: The ID of the blip.
+ required: true
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ blip[body]:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Blip"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Blip
+ operationId: deleteBlip
+ tags:
+ - Blips
+ security:
+ - basicAuth: []
+ description: |
+ You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the blip.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /blips/{id}/hide.json:
+ post:
+ summary: Hide Blip
+ operationId: hideBlip
+ tags:
+ - Blips
+ security:
+ - basicAuth: []
+ description: |
+ You must be the creator or Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the blip.
+ schema:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Blip"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /blips/{id}/unhide.json:
+ post:
+ summary: Unhide Blip
+ operationId: unhideBlip
+ tags:
+ - Blips
+ security:
+ - basicAuth: []
+ description: |
+ You must be Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the blip.
+ schema:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Blip"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /blips/{id}/warning.json:
+ post:
+ summary: Mark Blip
+ operationId: markBlip
+ tags:
+ - Blips
+ security:
+ - basicAuth: []
+ description: You must be Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the blip.
+ schema:
+ type: number
+ requestBody:
+ $ref: "#/components/requestBodies/warning"
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/DTextResponse"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Bulk Update Requests
+ /bulk_update_requests.json:
+ get:
+ summary: Search Bulk Update Requests
+ operationId: searchBulkUpdateRequests
+ tags:
+ - Bulk Update Requests
+ description: When no results are found, an object with an `bulk_update_requests` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - status_desc
+ - updated_at_desc
+ - updated_at_asc
+ - name: search[user_id]
+ in: query
+ schema:
+ type: number
+ - name: search[user_name]
+ in: query
+ schema:
+ type: string
+ - name: search[approver_id]
+ in: query
+ schema:
+ type: number
+ - name: search[approver_name]
+ in: query
+ schema:
+ type: string
+ - name: search[forum_topic_id]
+ in: query
+ schema:
+ type: number
+ - name: search[forum_post_id]
+ in: query
+ schema:
+ type: number
+ - name: search[status]
+ in: query
+ schema:
+ type: string
+ description: Multiple can be specified via comma separating.
+ enum:
+ - approved
+ - rejected
+ - pending
+ - name: search[title_matches]
+ in: query
+ schema:
+ type: string
+ - name: search[script_matches]
+ in: query
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/BulkUpdateRequest"
+ - type: object
+ description: No Results
+ required:
+ - bulk_update_requests
+ properties:
+ bulk_update_requests:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create Bulk Update Request
+ operationId: createBulkUpdateRequest
+ tags:
+ - Bulk Update Requests
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - bulk_update_request[script]
+ - bulk_update_request[title]
+ - bulk_update_request[reason]
+ properties:
+ bulk_update_request[script]:
+ type: string
+ bulk_update_request[title]:
+ type: string
+ bulk_update_request[reason]:
+ type: string
+ bulk_update_request[forum_topic_id]:
+ type: number
+ bulk_update_request[skip_forum]:
+ type: boolean
+ description: Only usable for Admin+
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/BulkUpdateRequest"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /bulk_update_request/{id}.json:
+ get:
+ summary: Get Bulk Update Request
+ operationId: getBulkUpdateRequest
+ tags:
+ - Bulk Update Requests
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the bulk update request.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/BulkUpdateRequest"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Bulk Update Request
+ operationId: editBulkUpdateRequest
+ tags:
+ - Bulk Update Requests
+ security:
+ - basicAuth: []
+ description: You must be the creator of the BUR, or Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the bulk update request.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ bulk_update_request[script]:
+ type: string
+ bulk_update_request[forum_topic_id]:
+ description: You must be Admin+.
+ type: string
+ bulk_update_request[forum_post_id]:
+ description: You must be Admin+.
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Reject Bulk Update Request
+ operationId: rejectBulkUpdateRequest
+ tags:
+ - Bulk Update Requests
+ security:
+ - basicAuth: []
+ description: You must be the creator of the BUR, or Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the bulk update request.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /bulk_update_request/{id}/approve.json:
+ post:
+ summary: Approve Bulk Update Request
+ operationId: approveBulkUpdateRequest
+ tags:
+ - Bulk Update Requests
+ description: Must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the bulk update request.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Comments
+ /comments.json:
+ get:
+ summary: Search Comments
+ operationId: searchComments
+ tags:
+ - Comments
+ description: For searching comments, group_by=comment must be set. When no results are found, an object with an `comments` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - $ref: "#/components/parameters/ip_addr"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - status
+ - status_desc
+ - updated_at_desc
+ - in: query
+ name: group_by
+ schema:
+ type: string
+ enum:
+ - comment
+ - post
+ - in: query
+ name: search[body_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[post_id]
+ description: Accepts a comma separated list.
+ schema:
+ type: number
+ - in: query
+ name: search[post_tags_match]
+ schema:
+ type: string
+ - in: query
+ name: search[post_note_updater_name]
+ schema:
+ type: string
+ - in: query
+ name: search[post_note_updater_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ schema:
+ type: string
+ - in: query
+ name: search[creator_id]
+ schema:
+ type: number
+ - in: query
+ name: search[is_sticky]
+ schema:
+ type: boolean
+ - in: query
+ name: search[is_hidden]
+ description: Only usable by Moderator+
+ schema:
+ type: boolean
+ - in: query
+ name: search[do_not_bump_post]
+ schema:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/Comment"
+ - type: object
+ description: No Results
+ required:
+ - comments
+ properties:
+ comments:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create Comment
+ operationId: createComment
+ tags:
+ - Comments
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - comment[body]
+ - comment[post_id]
+ properties:
+ comment[body]:
+ type: string
+ comment[post_id]:
+ type: number
+ comment[do_not_bump_post]:
+ type: boolean
+ comment[is_sticky]:
+ type: boolean
+ description: Only usable for Janitor+
+ comment[is_hidden]:
+ type: boolean
+ description: Only usable for Moderator+
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Comment"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /comments/{id}.json:
+ get:
+ summary: Get Comment
+ operationId: getComment
+ tags:
+ - Comments
+ description: If the comment is hidden, you must be the creator or Moderator+ to see it.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the comment.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Comment"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Comment
+ operationId: editComment
+ tags:
+ - Comments
+ security:
+ - basicAuth: []
+ description: |
+ You must be the creator of the comment, or Admin+ to edit. Marked comments cannot be edited.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the comment.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ comment[body]:
+ type: string
+ comment[is_sticky]:
+ type: boolean
+ description: Only usable for Janitor+
+ comment[is_hidden]:
+ type: boolean
+ description: Only usable for Moderator+
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Comment
+ operationId: deleteComment
+ tags:
+ - Comments
+ security:
+ - basicAuth: []
+ description: |
+ You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the comment.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /comments/{id}/hide.json:
+ post:
+ summary: Hide Comment
+ operationId: hideComment
+ tags:
+ - Comments
+ security:
+ - basicAuth: []
+ description: |
+ You must be the creator or Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the comment.
+ schema:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Comment"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /comments/{id}/unhide.json:
+ post:
+ summary: Unhide Comment
+ operationId: unhideComment
+ tags:
+ - Comments
+ security:
+ - basicAuth: []
+ description: |
+ You must be Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the comment.
+ schema:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Comment"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /comments/{id}/warning.json:
+ post:
+ summary: Mark Comment
+ operationId: markComment
+ tags:
+ - Comments
+ security:
+ - basicAuth: []
+ description: You must be Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the comment.
+ schema:
+ type: number
+ requestBody:
+ $ref: "#/components/requestBodies/warning"
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/DTextResponse"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Comment Votes
+ /comments/{id}/votes.json:
+ post:
+ summary: Create Comment Vote
+ operationId: createCommentVote
+ tags:
+ - Comment Votes
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the comment.
+ schema:
+ type: string
+ - name: score
+ in: query
+ required: true
+ schema:
+ type: number
+ enum:
+ - -1
+ - 1
+ - name: no_unvote
+ in: query
+ schema:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - score
+ - our_score
+ properties:
+ score:
+ type: number
+ our_score:
+ type: number
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Comment Vote
+ operationId: deleteCommentVote
+ tags:
+ - Comment Votes
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the comment.
+ schema:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /comment_votes/lock.json:
+ post:
+ summary: Lock Comment Votes
+ operationId: lockCommentVotes
+ tags:
+ - Comment Votes
+ security:
+ - basicAuth: []
+ description: |
+ You must be Moderator+. Errors if ids is not provided.
+ parameters:
+ - name: ids
+ in: query
+ required: true
+ description: The IDs of the comment votes, comma separated.
+ schema:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /comment_votes/delete.json:
+ post:
+ summary: Delete Comment Vote
+ operationId: deleteCommentVotes
+ tags:
+ - Comment Votes
+ security:
+ - basicAuth: []
+ description: |
+ You must be Admin+. Errors if ids is not provided.
+ parameters:
+ - name: ids
+ in: query
+ required: true
+ description: The IDs of the comment votes, comma separated.
+ schema:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ # DMails
+ /dmails.json:
+ get:
+ summary: Search DMails
+ operationId: searchDMails
+ tags:
+ - DMails
+ description: When no results are found, an object with a `dmails` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - in: query
+ name: search[title_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[message_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[to_name]
+ schema:
+ type: string
+ - in: query
+ name: search[to_id]
+ schema:
+ type: number
+ - in: query
+ name: search[from_name]
+ schema:
+ type: string
+ - in: query
+ name: search[from_id]
+ schema:
+ type: number
+ - in: query
+ name: search[is_read]
+ schema:
+ type: boolean
+ - in: query
+ name: search[is_deleted]
+ schema:
+ type: boolean
+ - in: query
+ name: search[read]
+ schema:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/DMail"
+ - type: object
+ description: No Results
+ required:
+ - dmails
+ properties:
+ dmails:
+ type: array
+ maxItems: 0
+ /dmails/{id}.json:
+ get:
+ summary: Get DMail
+ operationId: getDMail
+ tags:
+ - DMails
+ description: Fetching a dmail will not mark it as read.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the dmail.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/DMail"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ delete:
+ summary: Delete DMail
+ operationId: deleteDMail
+ tags:
+ - DMails
+ security:
+ - basicAuth: []
+ description: Deleting simply hides your copy of the dmail.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the dmail.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /dmails/{id}/mark_as_read.json:
+ put:
+ summary: Mark DMail As Read
+ operationId: markDMailAsRead
+ tags:
+ - DMails
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the dmail.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /dmails/{id}/mark_as_unread.json:
+ put:
+ summary: Mark DMail As Unread
+ operationId: markDMailAsUnread
+ tags:
+ - DMails
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the dmail.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /dmails/mark_all_as_unread.json:
+ put:
+ summary: Mark All DMails As Unread
+ operationId: markAllDMailsAsUnread
+ tags:
+ - DMails
+ security:
+ - basicAuth: []
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # DText Preview
+ /dtext_preview.json:
+ post:
+ summary: Preview DText
+ operationId: previewDText
+ tags:
+ - DText
+ description: Note while this route does not require auth, without auth it requires a CSRF token. For that reason it has been marked as requiring auth.
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - body
+ properties:
+ body:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/DTextResponse"
+ # Email Blacklists
+ /email_blacklists.json:
+ get:
+ summary: Search Email Blacklists
+ operationId: searchEmailBlacklists
+ tags:
+ - Email Blacklists
+ description: You must be Admin+. When no results are found, an object with an `email_blacklists` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - reason
+ - domain
+ - in: query
+ name: search[domain]
+ schema:
+ type: string
+ - in: query
+ name: search[reason]
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/EmailBlacklist"
+ - type: object
+ description: No Results
+ required:
+ - email_blacklists
+ properties:
+ email_blacklists:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create Email Blacklist
+ operationId: createEmailBlacklist
+ tags:
+ - Email Blacklists
+ description: You must be Admin+.
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - email_blacklist[domain]
+ - email_blacklist[reason]
+ properties:
+ email_blacklist[domain]:
+ type: string
+ email_blacklist[reason]:
+ type: string
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/EmailBlacklist"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /email_blacklists/{id}.json:
+ delete:
+ summary: Delete Email Blacklist
+ operationId: deleteEmailBlacklist
+ tags:
+ - Email Blacklists
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the email blacklist.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Favorites
+ /favorites.json:
+ get:
+ summary: List Favorites
+ operationId: listFavorites
+ tags:
+ - Favorites
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - in: query
+ name: user_id
+ description: You must be the user or Moderator+ if the user has their favorites hidden.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - posts
+ properties:
+ posts:
+ type: array
+ items:
+ $ref: "#/components/schemas/Post"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ post:
+ summary: Add Favorite
+ operationId: addFavorite
+ tags:
+ - Favorites
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - post_id
+ properties:
+ post_ud:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - post
+ properties:
+ post:
+ $ref: "#/components/schemas/Post"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /favorites/{id}.json:
+ delete:
+ summary: Remove Favorite
+ operationId: removeFavorite
+ tags:
+ - Favorites
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ # Forum Post
+ /forum_posts.json:
+ get:
+ summary: Search Forum Posts
+ operationId: searchForumPosts
+ tags:
+ - Forum Posts
+ description: When no results are found, an object with an `forum_posts` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - in: query
+ name: search[creator_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ schema:
+ type: string
+ - in: query
+ name: search[topic_id]
+ schema:
+ type: number
+ - in: query
+ name: search[topic_title_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[body_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[topic_category_id]
+ schema:
+ type: number
+ - in: query
+ name: search[is_hidden]
+ schema:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/ForumPost"
+ - type: object
+ description: No Results
+ required:
+ - forum_posts
+ properties:
+ forum_posts:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create Forum Post
+ operationId: createForumPost
+ tags:
+ - Forum Posts
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - forum_post[body]
+ - forum_post[topic_id]
+ properties:
+ forum_post[body]:
+ type: string
+ forum_post[topic_id]:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ForumPost"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /forum_posts/{id}.json:
+ get:
+ summary: Get Forum Post
+ operationId: getForumPost
+ tags:
+ - Forum Posts
+ description: If the forum post is hidden, you must be the creator or Moderator+ to see it.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum post.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ForumPost"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Forum Post
+ operationId: editForumPost
+ tags:
+ - Forum Posts
+ security:
+ - basicAuth: []
+ description: You must be the creator of the forum post, or Admin+ to edit. Marked forum posts cannot be edited.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum post.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ forum_post[body]:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Forum Post
+ operationId: deleteForumPost
+ tags:
+ - Forum Posts
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum post.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /forum_posts/{id}/hide.json:
+ post:
+ summary: Hide Forum Post
+ operationId: hideForumPost
+ tags:
+ - Forum Posts
+ security:
+ - basicAuth: []
+ description: You must be the creator or Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum post.
+ schema:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ForumPost"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /forum_posts/{id}/unhide.json:
+ post:
+ summary: Unhide Forum Post
+ operationId: unhideForumPost
+ tags:
+ - Forum Posts
+ security:
+ - basicAuth: []
+ description: You must be Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum post.
+ schema:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ForumPost"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /forum_posts/{id}/warning.json:
+ post:
+ summary: Mark Forum Post
+ operationId: markForumPost
+ tags:
+ - Forum Posts
+ security:
+ - basicAuth: []
+ description: |
+ You must be Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum post.
+ schema:
+ type: number
+ requestBody:
+ $ref: "#/components/requestBodies/warning"
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/DTextResponse"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Forum Post Votes
+ /forum_posts/{id}/votes.json:
+ post:
+ summary: Create Forum Post Vote
+ operationId: createForumPostVote
+ tags:
+ - Forum Post Votes
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum post.
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - forum_post_vote[score]
+ properties:
+ forum_post_vote[score]:
+ type: number
+ enum:
+ - -1
+ - 0
+ - 1
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ForumPostVote"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Forum Post Vote
+ operationId: deleteForumPostVote
+ tags:
+ - Forum Post Votes
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum post.
+ schema:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ # Forum Topics
+ /forum_topics.json:
+ get:
+ summary: Search Forum Topics
+ operationId: searchForumTopics
+ tags:
+ - Forum Topics
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - sticky
+ - in: query
+ name: search[title]
+ schema:
+ type: string
+ - in: query
+ name: search[title_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[category_id]
+ schema:
+ type: number
+ - in: query
+ name: search[is_sticky]
+ schema:
+ type: boolean
+ - in: query
+ name: search[is_locked]
+ schema:
+ type: boolean
+ - in: query
+ name: search[is_hidden]
+ schema:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/ForumTopic"
+ post:
+ summary: Create Forum Topic
+ operationId: createForumTopic
+ tags:
+ - Forum Topics
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - forum_topic[title]
+ - forum_topic[category_id]
+ properties:
+ forum_topic[title]:
+ type: string
+ forum_topic[category_id]:
+ type: number
+ forum_topic[original_post_attributes][id]:
+ type: number
+ description: Forum post ID. Mutually exclusive with body, one must be provided.
+ forum_topic[original_post_attributes][body]:
+ type: string
+ description: First forum post body. Mutually exclusive with id, one must be provided.
+ forum_topic[is_sticky]:
+ type: boolean
+ description: You must be Moderator+.
+ forum_topic[is_locked]:
+ type: boolean
+ description: You must be Moderator+.
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ForumTopic"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /forum_topics/{id}.json:
+ get:
+ summary: Get Forum Forum Topic
+ operationId: getForumTopic
+ tags:
+ - Forum Topics
+ description: If the forum topic is hidden, you must be the creator or Moderator+ to see it.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum topic.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ForumTopic"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Forum Topic
+ operationId: editForumTopic
+ tags:
+ - Forum Topics
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum topic.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ forum_topic[title]:
+ type: string
+ forum_topic[category_id]:
+ type: number
+ forum_topic[original_post_attributes][id]:
+ type: number
+ description: Forum post ID. Silently ignored
+ forum_topic[original_post_attributes][body]:
+ type: string
+ description: First forum post body.
+ forum_topic[is_sticky]:
+ type: boolean
+ description: You must be Moderator+.
+ forum_topic[is_locked]:
+ type: boolean
+ description: You must be Moderator+.
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Forum Topic
+ operationId: deleteForumTopic
+ tags:
+ - Forum Topics
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum topic.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /forum_topics/{id}/hide.json:
+ post:
+ summary: Hide Forum Topic
+ operationId: hideForumTopic
+ tags:
+ - Forum Topics
+ security:
+ - basicAuth: []
+ description: You must be the creator or Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum topic.
+ schema:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ForumTopic"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /forum_topics/{id}/unhide.json:
+ post:
+ summary: Unhide Forum Topic
+ operationId: unhideForumTopic
+ tags:
+ - Forum Topics
+ security:
+ - basicAuth: []
+ description: You must be Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum topic.
+ schema:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ForumTopic"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /forum_topics/{id}/subscribe.json:
+ post:
+ summary: Subscribe To Forum Topic
+ operationId: subscribeForumTopic
+ tags:
+ - Forum Topics
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum topic.
+ schema:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ForumTopic"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /forum_topics/{id}/unsubscribe.json:
+ post:
+ summary: Unsubscribe From Forum Topic
+ operationId: unsubscribeForumTopic
+ tags:
+ - Forum Topics
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the forum topic.
+ schema:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ForumTopic"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /forum_topics/mark_all_as_read.json:
+ post:
+ summary: Mark All Forum Topics As Read
+ operationId: markAllForumTopicsAsRead
+ tags:
+ - Forum Topics
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - category_id
+ properties:
+ category_id:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ # Help Pages
+ /help.json:
+ get:
+ summary: List Help Pages
+ operationId: listHelpPages
+ description: Will error if no help pages exist.
+ tags:
+ - Help Pages
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/Help"
+ post:
+ summary: Create Help Page
+ operationId: createHelpPage
+ tags:
+ - Help Pages
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - help_page[name]
+ - help_page[wiki_page]
+ properties:
+ help_page[name]:
+ type: string
+ help_page[wiki_page]:
+ type: string
+ help_page[related]:
+ type: string
+ description: Separate with a comma followed by a space.
+ help_page[title]:
+ type: string
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Help"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /help/{id}.json:
+ get:
+ summary: Get Help Page
+ operationId: getHelpPage
+ tags:
+ - Help Pages
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID or name of the help page.
+ schema:
+ type: ["string", "number"]
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Help"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Help Page
+ operationId: editHelpPage
+ tags:
+ - Help Pages
+ security:
+ - basicAuth: []
+ description: You must be Admin+
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the help page.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ help_page[name]:
+ type: string
+ help_page[wiki_page]:
+ type: string
+ help_page[related]:
+ type: string
+ description: Separate with a comma followed by a space.
+ help_page[title]:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Help Page
+ operationId: deleteHelpPage
+ tags:
+ - Help Pages
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the help page.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # IP Bans
+ /ip_bans.json:
+ get:
+ summary: List IP Bans
+ operationId: listIPBans
+ tags:
+ - IP Bans
+ security:
+ - basicAuth: []
+ description: You must be Admin+. When no results are found, an object with an `ip_bans` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - $ref: "#/components/parameters/ip_addr"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - in: query
+ name: search[banner_id]
+ schema:
+ type: number
+ - in: query
+ name: search[banner_name]
+ schema:
+ type: string
+ - in: query
+ name: search[reason]
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/IPBan"
+ - type: object
+ description: No Results
+ required:
+ - ip_bans
+ properties:
+ ip_bans:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create IP Ban
+ operationId: createIPBan
+ tags:
+ - IP Bans
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - ip_ban[ip_addr]
+ - ip_ban[reason]
+ properties:
+ ip_ban[ip_addr]:
+ type: string
+ ip_ban[reason]:
+ type: string
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/IPBan"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /ip_bans/{id}.json:
+ delete:
+ summary: Delete IP Ban
+ operationId: deleteIPBan
+ tags:
+ - IP Bans
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the ip ban.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # IQDB
+ /iqdb_queries.json:
+ get:
+ summary: Query IQDB
+ operationId: queryIQDBGet
+ tags:
+ - IQDB
+ parameters:
+ - name: search[score_cutoff]
+ in: query
+ schema:
+ type: number
+ - name: search[url]
+ in: query
+ schema:
+ type: string
+ - name: search[post_id]
+ in: query
+ schema:
+ type: number
+ - name: search[hash]
+ in: query
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - posts
+ properties:
+ posts:
+ type: array
+ items:
+ $ref: "#/components/schemas/IQDBResponse"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ post:
+ summary: Query IQDB
+ operationId: queryIQDPost
+ tags:
+ - IQDB
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ properties:
+ search[file]:
+ type: string
+ format: binary
+ search[score_cutoff]:
+ type: number
+ search[url]:
+ type: string
+ search[post_id]:
+ type: string
+ search[hash]:
+ type: string
+ application/json:
+ schema:
+ type: object
+ properties:
+ score_cutoff:
+ type: number
+ url:
+ type: string
+ post_id:
+ type: string
+ hash:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - posts
+ properties:
+ posts:
+ type: array
+ items:
+ $ref: "#/components/schemas/IQDBResponse"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ # Mascots
+ /mascots.json:
+ get:
+ summary: Search Mascots
+ operationId: searchMascots
+ tags:
+ - Mascots
+ description: When no results are found, an object with an `mascots` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/Mascot"
+ - type: object
+ description: No Results
+ required:
+ - mascots
+ properties:
+ mascots:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create Mascot
+ operationId: createMascot
+ tags:
+ - Mascots
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ required:
+ - mascot[mascot_file]
+ - mascot[display_name]
+ - mascot[background_color]
+ - mascot[artist_url]
+ - mascot[artist_name]
+ properties:
+ mascot[mascot_file]:
+ type: string
+ format: binary
+ mascot[display_name]:
+ type: string
+ mascot[background_color]:
+ type: string
+ mascot[artist_url]:
+ type: string
+ mascot[artist_name]:
+ type: string
+ mascot[available_on_string]:
+ type: string
+ description: Comma separated site names.
+ mascot[active]:
+ type: boolean
+ mascot[hide_anonymous]:
+ type: boolean
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Mascot"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /mascots/{id}.json:
+ patch:
+ summary: Edit Mascot
+ operationId: editMascot
+ tags:
+ - Mascots
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the mascot.
+ schema:
+ type: number
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ properties:
+ mascot[mascot_file]:
+ type: string
+ format: binary
+ mascot[display_name]:
+ type: string
+ mascot[background_color]:
+ type: string
+ mascot[artist_url]:
+ type: string
+ mascot[artist_name]:
+ type: string
+ mascot[available_on_string]:
+ type: string
+ description: Comma separated site names.
+ mascot[active]:
+ type: boolean
+ mascot[hide_anonymous]:
+ type: boolean
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Mascot
+ operationId: deleteMascot
+ tags:
+ - Mascots
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the mascot.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Mod Actions
+ /mod_actions.json:
+ get:
+ summary: Search Mod Actions
+ operationId: searchModActions
+ tags:
+ - Mod Actions
+ description: |
+ Note that some mod actions are not in use anymore. Their entries exist for historical purposes only.
+ The current legacy actions are as follows:
+ * `created_positive_record`, `created_neutral_record`, `created_negative_record`
+ * `created_flag_reason`, `edited_flag_reason`, `deleted_flag_reason`
+ * `post_move_favorites`, `post_delete`, `post_undelete`, `post_destroy`, `post_rating_lock`, `post_unapprove`
+ * `post_replacement_accept`, `post_replacement_reject`, `post_replacement_delete`
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - in: query
+ name: search[creator_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ schema:
+ type: string
+ - in: query
+ name: search[action]
+ schema:
+ type: string
+ enum: *modaction-actions
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/ModAction"
+ # News Updates
+ /news_updates.json:
+ get:
+ summary: List News Updates
+ operationId: listNewsUpdates
+ tags:
+ - News Updates
+ description: When no results are found, an object with an `news_updates` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/NewsUpdate"
+ - type: object
+ description: No Results
+ required:
+ - news_updates
+ properties:
+ news_updates:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create News Update
+ operationId: createNewsUpdate
+ tags:
+ - News Updates
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - news_update[message]
+ properties:
+ news_update[message]:
+ type: string
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/NewsUpdate"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /news_updates/{id}.json:
+ patch:
+ summary: Edit News Update
+ operationId: editNewsUpdate
+ tags:
+ - News Updates
+ security:
+ - basicAuth: []
+ description: You must be Admin+
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the news update.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - news_update[message]
+ properties:
+ news_update[message]:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete News Update
+ operationId: deleteNewsUpdate
+ tags:
+ - News Updates
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the news update.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Notes
+ /notes.json:
+ get:
+ summary: Search Notes
+ operationId: searchNotes
+ tags:
+ - Notes
+ description: When no results are found, an object with an `notes` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - in: query
+ name: search[body_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[is_active]
+ schema:
+ type: boolean
+ - in: query
+ name: search[post_id]
+ schema:
+ type: number
+ - in: query
+ name: search[post_tags_match]
+ schema:
+ type: string
+ - in: query
+ name: search[post_note_updater_id]
+ schema:
+ type: number
+ - in: query
+ name: search[post_note_updater_name]
+ schema:
+ type: string
+ - in: query
+ name: search[creator_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/Note"
+ - type: object
+ description: No Results
+ required:
+ - notes
+ properties:
+ notes:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create Note
+ operationId: createNote
+ tags:
+ - Notes
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - note[post_id]
+ - note[x]
+ - note[y]
+ - note[width]
+ - note[height]
+ - note[body]
+ properties:
+ note[post_id]:
+ type: number
+ note[x]:
+ type: number
+ note[y]:
+ type: number
+ note[width]:
+ type: number
+ note[height]:
+ type: number
+ note[body]:
+ type: string
+ note[html_id]:
+ type: string
+ description: Passthrough, used in frontend.
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ allOf:
+ - $ref: "#/components/schemas/Note"
+ - type: object
+ required:
+ - html_id
+ properties:
+ html_id:
+ type: string
+ description: Passthrough, used in frontend.
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /notes/{id}.json:
+ get:
+ summary: Get Note
+ operationId: getNote
+ tags:
+ - Notes
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the note.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Note"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Note
+ operationId: editNote
+ tags:
+ - Notes
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the note.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ note[x]:
+ type: number
+ note[y]:
+ type: number
+ note[width]:
+ type: number
+ note[height]:
+ type: number
+ note[body]:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Note
+ operationId: deleteNote
+ tags:
+ - Notes
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the note.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /notes/{id}/revert.json:
+ post:
+ summary: Revert Note
+ operationId: revertNote
+ tags:
+ - Notes
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the note.
+ schema:
+ type: number
+ - name: version_id
+ in: query
+ required: true
+ description: The version ID to revert to.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ # Note Versions
+ /note_versions.json:
+ get:
+ summary: Search Note Versions
+ operationId: searchNoteVersions
+ tags:
+ - Note Versions
+ description: When no results are found, an object with an `note_versions` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - $ref: "#/components/parameters/ip_addr"
+ - name: search[order]
+ in: query
+ description: The order of the results.
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - in: query
+ name: search[updater_id]
+ schema:
+ type: number
+ - in: query
+ name: search[updater_name]
+ schema:
+ type: string
+ - in: query
+ name: search[post_id]
+ schema:
+ type: number
+ - in: query
+ name: search[note_id]
+ schema:
+ type: number
+ - in: query
+ name: search[is_active]
+ schema:
+ type: boolean
+ - in: query
+ name: search[body_matches]
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/NoteVersion"
+ - type: object
+ description: No Results
+ required:
+ - note_versions
+ properties:
+ note_versions:
+ type: array
+ maxItems: 0
+ # Pools
+ /pools.json:
+ get:
+ summary: Search Pools
+ operationId: searchPools
+ tags:
+ - Pools
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - name
+ - created_at
+ - post_count
+ - in: query
+ name: search[name_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[description_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[creator_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ schema:
+ type: string
+ - in: query
+ name: search[category]
+ schema:
+ type: string
+ enum: *pool-categories
+ - in: query
+ name: search[is_active]
+ schema:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/Pool"
+ post:
+ summary: Create Pool
+ operationId: createPool
+ tags:
+ - Pools
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - pool[name]
+ properties:
+ pool[name]:
+ type: string
+ pool[description]:
+ type: string
+ pool[category]:
+ type: string
+ enum: *pool-categories
+ ipool[s_active]:
+ type: boolean
+ pool[post_ids_string]:
+ type: string
+ description: Space separated list of post IDs. Mutually exclusive with post_ids.
+ pool[post_ids]:
+ type: array
+ description: Array of post IDs. Mutually exclusive with post_ids_string.
+ items:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Pool"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /pools/{id}.json:
+ get:
+ summary: Get Pool
+ operationId: getPool
+ tags:
+ - Pools
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the pool.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Pool"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Pool
+ operationId: editPool
+ tags:
+ - Pools
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the pool.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ pool[name]:
+ type: string
+ pool[description]:
+ type: string
+ pool[is_active]:
+ type: boolean
+ pool[category]:
+ type: string
+ enum: *pool-categories
+ description: If the pool has more than 30 posts, you must be Janitor+.
+ pool[post_ids_string]:
+ type: string
+ description: Space separated list of post IDs. Mutually exclusive with post_ids.
+ pool[post_ids]:
+ type: array
+ description: Array of post IDs. Mutually exclusive with post_ids_string.
+ items:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Pool
+ operationId: deletePool
+ tags:
+ - Pools
+ description: You must be Janitor+.
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the pool.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /pools/{id}/revert.json:
+ put:
+ summary: Revert Pool
+ operationId: revertPool
+ tags:
+ - Pools
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the pool.
+ schema:
+ type: number
+ - name: version_id
+ in: query
+ required: true
+ description: The version ID to revert to.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ # Pool Element
+ /pool_element.js:
+ post:
+ summary: Add Post To Pool
+ operationId: addPostToPool
+ tags:
+ - Pools
+ security:
+ - basicAuth: []
+ description: Note that the extension is JS, this route will not return JSON.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - post_id
+ properties:
+ pool_id:
+ type: number
+ description: Mutually exclusive with pool_name.
+ pool_name:
+ type: string
+ description: Mutually exclusive with pool_id.
+ post_id:
+ type: number
+ responses:
+ 200:
+ description: Success
+ /pool_element.json:
+ delete:
+ summary: Remove Post From Pool
+ operationId: removePostFromPool
+ tags:
+ - Pools
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - pool_id
+ - post_id
+ properties:
+ pool_id:
+ type: number
+ post_id:
+ type: number
+ responses:
+ 204:
+ description: Success
+ # Pool Versions
+ /pool_versions.json:
+ get:
+ summary: Search Pool Versions
+ operationId: searchPoolVersions
+ tags:
+ - Pool Versions
+ description: When no results are found, an object with an `pool_versions` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - $ref: "#/components/parameters/ip_addr"
+ - name: search[order]
+ in: query
+ description: The order of the results.
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - in: query
+ name: search[updater_id]
+ schema:
+ type: number
+ - in: query
+ name: search[updater_name]
+ schema:
+ type: string
+ - in: query
+ name: search[pool_id]
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/PoolVersion"
+ - type: object
+ description: No Results
+ required:
+ - pool_versions
+ properties:
+ pool_versions:
+ type: array
+ maxItems: 0
+ # Popular
+ /popular.json:
+ get:
+ summary: List Most Upvoted Posts
+ operationId: listPopular
+ tags:
+ - Popular
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - name: date
+ in: query
+ description: The date to list popular uploads for. Only The day, month, and year are considered.
+ schema:
+ type: string
+ format: date
+ - name: scale
+ in: query
+ description: The scale of the results, in relation to `date`.
+ schema:
+ type: string
+ enum:
+ - month
+ - week
+ - day
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - posts
+ properties:
+ posts:
+ type: array
+ items:
+ $ref: "#/components/schemas/Post"
+ # Posts
+ /posts.json:
+ get:
+ summary: Search Posts
+ operationId: searchPosts
+ tags:
+ - Posts
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - in: query
+ name: tags
+ schema:
+ type: string
+ - in: query
+ name: md5
+ schema:
+ type: string
+ - in: query
+ name: random
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - posts
+ properties:
+ posts:
+ type: array
+ items:
+ $ref: "#/components/schemas/Post"
+ /posts/{id}.json:
+ get:
+ summary: Get Post
+ operationId: getPost
+ tags:
+ - Posts
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - post
+ properties:
+ post:
+ $ref: "#/components/schemas/Post"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Post
+ operationId: editPost
+ tags:
+ - Posts
+ security:
+ - basicAuth: []
+ description: Most errors are silently swallowed.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ post[tag_string]:
+ type: string
+ description: Replaces all tags on the post.
+ post[old_tag_string]:
+ type: string
+ description: The tag string before your edits, used to reconcile conflicts.
+ post[tag_string_diff]:
+ type: string
+ description: Tags with a minus are removed, else they are added. Mutually exclusive with tag_string.
+ post[source_diff]:
+ type: string
+ description: Sources with a minus are removed, else they are added. It is not possible to add inactive sources through this. Mutually exclusive with source.
+ post[source]:
+ type: string
+ description: Replaces all sources on the post.
+ post[old_source]:
+ type: string
+ description: The sources before your edits, used to reconcile conflicts.
+ post[parent_id]:
+ type: number
+ post[old_parent_id]:
+ type: number
+ post[description]:
+ type: string
+ post[old_description]:
+ type: string
+ post[rating]:
+ type: string
+ enum: *ratings
+ post[old_rating]:
+ type: string
+ enum: *ratings
+ post[edit_reason]:
+ type: string
+ post[is_rating_locked]:
+ type: boolean
+ description: You must be Privileged+.
+ post[is_note_locked]:
+ type: boolean
+ description: You must be Janitor+.
+ post[bg_colo]r:
+ type: string
+ description: You must be Janitor+.
+ post[is_comment_locked]:
+ type: boolean
+ description: You must be Admin+.
+ post[is_status_locked]:
+ type: boolean
+ description: You must be Admin+.
+ post[locked_tags]:
+ type: string
+ description: You must be Admin+.
+ post[hide_from_anonymous]:
+ type: boolean
+ description: You must be Admin+.
+ post[hide_from_search_engines]:
+ type: boolean
+ description: You must be Admin+.
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Post"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /posts/{id}/update_iqdb.json:
+ get:
+ summary: Update Post IQDB
+ operationId: updatePostIqdb
+ tags:
+ - Posts
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - post
+ properties:
+ post:
+ $ref: "#/components/schemas/Post"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /posts/{id}/mark_as_translated.json:
+ post:
+ summary: Mark Post As Translated
+ operationId: markPostAsTranslated
+ tags:
+ - Posts
+ security:
+ - basicAuth: []
+ description: Will error if no body is provided.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ translation_check:
+ type: boolean
+ partially_translated:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - post
+ properties:
+ post:
+ $ref: "#/components/schemas/Post"
+
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /posts/{id}/copy_notes.json:
+ put:
+ summary: Copy Notes To Post
+ operationId: copyNotesToPost
+ tags:
+ - Posts
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - other_post_id
+ properties:
+ other_post_id:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 400:
+ $ref: "#/components/responses/MessageError"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /posts/{id}/revert.json:
+ post:
+ summary: Revert Post
+ operationId: revertPost
+ tags:
+ - Posts
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: number
+ - name: version_id
+ in: query
+ required: true
+ description: The version ID to revert to.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /posts/{id}/show_seq.json:
+ get:
+ summary: Get Post In Sequence
+ operationId: getPostInSequence
+ tags:
+ - Posts
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: number
+ - name: seq
+ in: query
+ description: The direction to move in the sequence.
+ schema:
+ type: string
+ enum:
+ - next
+ - prev
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - post
+ properties:
+ post:
+ $ref: "#/components/schemas/Post"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /posts/{id}/flag.json:
+ delete:
+ summary: Unflag Post
+ operationId: unflagPost
+ tags:
+ - Posts
+ description: You must have the "Approve Posts" permission.
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ approval:
+ type: string
+ description: Approves the post if set to "approve".
+
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /posts/random.json:
+ get:
+ summary: Get Random Post
+ operationId: getRandomPost
+ tags:
+ - Posts
+ parameters:
+ - name: tags
+ in: query
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - post
+ properties:
+ post:
+ $ref: "#/components/schemas/Post"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Post Sets
+ /post_sets.json:
+ get:
+ summary: Search Post Sets
+ operationId: searchPostSets
+ tags:
+ - Post Sets
+ description: When no results are found, an object with a `post_sets` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - name
+ - shortname
+ - post_count
+ - postcount
+ - created_at
+ - updated_at
+ - update
+ - in: query
+ name: search[creator_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ schema:
+ type: string
+ - in: query
+ name: search[name]
+ schema:
+ type: string
+ - in: query
+ name: search[shortname]
+ schema:
+ type: string
+ - in: query
+ name: search[is_public]
+ description: You must be Moderator+.
+ schema:
+ type: boolean
+ - in: query
+ name: search[post_id]
+ schema:
+ type: number
+ - in: query
+ name: search[maintainer_id]
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/PostSet"
+ - type: object
+ description: No Results
+ required:
+ - post_sets
+ properties:
+ post_sets:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create Post Set
+ operationId: createPostSet
+ tags:
+ - Post Sets
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - post_set[name]
+ - post_set[shortname]
+ properties:
+ post_set[name]:
+ type: string
+ post_set[shortname]:
+ type: string
+ post_set[description]:
+ type: string
+ post_set[is_public]:
+ type: boolean
+ post_set[ransfer_on_delete]:
+ type: boolean
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/PostSet"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /post_sets/{id}.json:
+ get:
+ summary: Get Post Set
+ operationId: getPostSet
+ tags:
+ - Post Sets
+ description: You must be Moderator+ if the set is not public.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post set.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/PostSet"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Post Set
+ operationId: editPostSet
+ tags:
+ - Post Sets
+ security:
+ - basicAuth: []
+ description: You must be the owner of the set, or Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post sets.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ post_set[name]:
+ type: string
+ post_set[shortname]:
+ type: string
+ post_set[description]:
+ type: string
+ post_set[is_public]:
+ type: boolean
+ post_set[transfer_on_delete]:
+ type: boolean
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Post Set
+ operationId: deletePostSet
+ tags:
+ - Post Sets
+ security:
+ - basicAuth: []
+ description: You must be the owner of the set, or Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post set.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /post_sets/{id}/update_posts.json:
+ post:
+ summary: Update Post Set Posts
+ operationId: updatePostSetPosts
+ tags:
+ - Post Sets
+ security:
+ - basicAuth: []
+ description: You must be the owner of the set, a maintainer (if public), or Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post set.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - post_set[post_ids_string]
+ properties:
+ post_set[post_ids_string]:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/PostSet"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /post_sets/{id}/add_posts.json:
+ post:
+ summary: Add Posts To Post Set
+ operationId: addPostsToPostSet
+ tags:
+ - Post Sets
+ security:
+ - basicAuth: []
+ description: You must be the owner of the set, a maintainer (if public), or Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post set.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - post_ids
+ properties:
+ post_ids:
+ type: array
+ description: post_ids[]=1&post_ids[]=2
+ items:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/PostSet"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /post_sets/{id}/remove_posts.json:
+ post:
+ summary: Remove Posts From Post Set
+ operationId: removePostsFromPostSet
+ tags:
+ - Post Sets
+ security:
+ - basicAuth: []
+ description: You must be the owner of the set, a maintainer (if public), or Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post set.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - post_ids
+ properties:
+ post_ids:
+ type: array
+ description: post_ids[]=1&post_ids[]=2
+ items:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/PostSet"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /post_sets/for_select.json:
+ get:
+ summary: List Post Sets For Select
+ operationId: listPostSetsForSelect
+ tags:
+ - Post Sets
+ security:
+ - basicAuth: []
+ description: You must be the owner of the set, a maintainer (if public), or Admin+.
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ Owned:
+ type: array
+ items:
+ anyOf:
+ - type: string
+ - type: number
+ Maintained:
+ type: array
+ items:
+ anyOf:
+ - type: string
+ - type: number
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ # Post Approvals
+ /post_approvals.json:
+ get:
+ summary: Search Post Approvals
+ operationId: searchPostApprovals
+ tags:
+ - Post Approvals
+ description: When no results are found, an object with an `post_approvals` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - name: search[post_tags_match]
+ in: query
+ schema:
+ type: string
+ - name: search[user_id]
+ in: query
+ schema:
+ type: number
+ - name: search[user_name]
+ in: query
+ schema:
+ type: string
+ - name: search[post_id]
+ in: query
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/PostApproval"
+ - type: object
+ description: No Results
+ required:
+ - post_aprovals
+ properties:
+ post_approvals:
+ type: array
+ maxItems: 0
+ # Post Events
+ /post_events.json:
+ get:
+ summary: Search Post Events
+ operationId: searchPostEvents
+ tags:
+ - Post Events
+ security:
+ - basicAuth: []
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - in: query
+ name: search[post_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ schema:
+ type: string
+ - in: query
+ name: search[action]
+ schema:
+ type: string
+ enum: *post-event-actions
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - post_events
+ properties:
+ post_events:
+ type: array
+ items:
+ $ref: "#/components/schemas/PostEvent"
+ # Post Flags
+ /post_flags.json:
+ get:
+ summary: Search Post Flags
+ operationId: searchPostFlags
+ tags:
+ - Post Flags
+ description: When no results are found, an object with an `post_flags` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - $ref: "#/components/parameters/ip_addr"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - in: query
+ name: search[reason_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[creator_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ schema:
+ type: string
+ - in: query
+ name: search[post_id]
+ schema:
+ type: number
+ - in: query
+ name: search[post_tags_match]
+ schema:
+ type: string
+ - in: query
+ name: search[type]
+ schema:
+ type: string
+ - in: query
+ name: search[is_resolved]
+ schema:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/PostFlag"
+ - type: object
+ description: No Results
+ required:
+ - post_flags
+ properties:
+ post_flags:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create Post Flag
+ operationId: createPostFlag
+ tags:
+ - Post Flags
+ security:
+ - basicAuth: []
+ description: Will error if post_id is not provided.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - post_flag[post_id]
+ - post_flag[reason_name]
+ properties:
+ post_flag[post_id]:
+ type: number
+ post_flag[reason_name]:
+ type: string
+ enum:
+ - uploading_guidelines
+ - young_human
+ - dnp_artist
+ - pay_content
+ - trace
+ - previously_deleted
+ - real_porn
+ - corrupt
+ - inferior
+ post_flag[parent_id]:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/PostFlag"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /post_flags/{id}.json:
+ get:
+ summary: Get Post Flag
+ operationId: getPostFlag
+ tags:
+ - Post Flags
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post flag.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/PostFlag"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Post Replacements
+ /post_replacements.json:
+ get:
+ summary: Search Post Replacements
+ operationId: searchPostReplacements
+ tags:
+ - Post Replacements
+ description: When no results are found, an object with an `post_replacements` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - in: query
+ name: search[file_ext]
+ schema:
+ type: string
+ - in: query
+ name: search[md5]
+ schema:
+ type: string
+ - in: query
+ name: search[status]
+ schema:
+ type: string
+ - in: query
+ name: search[creator_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ schema:
+ type: string
+ - in: query
+ name: search[approver_id]
+ schema:
+ type: number
+ - in: query
+ name: search[approver_name]
+ schema:
+ type: string
+ - in: query
+ name: search[rejector_id]
+ schema:
+ type: number
+ - in: query
+ name: search[rejector_name]
+ schema:
+ type: string
+ - in: query
+ name: search[uploader_name_on_approve]
+ schema:
+ type: string
+ - in: query
+ name: search[uploader_id_on_approve]
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/PostReplacement"
+ - type: object
+ description: No Results
+ required:
+ - post_replacements
+ properties:
+ post_replacements:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create Post Replacement
+ operationId: createPostReplacement
+ tags:
+ - Post Replacements
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ required:
+ - post_replacement[reason]
+ properties:
+ post_replacement[replacement_file]:
+ type: string
+ format: binary
+ description: Mutually exclusive with replacement_url.
+ post_replacement[replacement_url]:
+ type: string
+ description: Mutually exclusive with replacement_file.
+ post_replacement[reason]:
+ type: string
+ post_replacement[source]:
+ type: string
+ post_replacement[as_pending]:
+ type: boolean
+ description: You must be Janitor+.
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - success
+ - location
+ properties:
+ success:
+ type: boolean
+ enum:
+ - true
+ location:
+ type: string
+ example: "/posts/1"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 412:
+ $ref: "#/components/responses/MessageError"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /post_replacements/{id}.json:
+ delete:
+ summary: Delete Post Replacement
+ operationId: deletePostReplacement
+ tags:
+ - Post Replacements
+ description: You must be Admin+.
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post replacement.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /post_replacements/{id}/approve.json:
+ put:
+ summary: Approve Post Replacement
+ operationId: approvePostReplacement
+ tags:
+ - Post Replacements
+ security:
+ - basicAuth: []
+ description: You must have the "Approve Posts" permission.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post replacement.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /post_replacements/{id}/reject.json:
+ put:
+ summary: Reject Post Replacement
+ operationId: rejectPostReplacement
+ tags:
+ - Post Replacements
+ security:
+ - basicAuth: []
+ description: You must have the "Approve Posts" permission.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post replacement.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ reason:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /post_replacements/{id}/promote.json:
+ post:
+ summary: Promote Post Replacement
+ operationId: promotePostReplacement
+ tags:
+ - Post Replacements
+ security:
+ - basicAuth: []
+ description: You must have the "Approve Posts" permission.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post replacement.
+ schema:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - post
+ properties:
+ post:
+ $ref: "#/components/schemas/Post"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /post_replacements/{id}/toggle_penalize.json:
+ put:
+ summary: Toggle Post Replacement Penalty
+ operationId: togglePostReplacementPenalty
+ tags:
+ - Post Replacements
+ security:
+ - basicAuth: []
+ description: You must have the "Approve Posts" permission.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post replacement.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ # Post Versions
+ /post_versions.json:
+ get:
+ summary: Search Post Versions
+ operationId: searchPostVersions
+ tags:
+ - Post Versions
+ description: Errors if no results are found.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - name: page
+ in: query
+ description: The page number of results to get. Between 1 and 750. Note that for post versions specifically, you can only go through the 10,000 most recent results with page numbers.
+ schema:
+ type: number
+ minimum: 1
+ maximum: 750
+ - $ref: "#/components/parameters/id"
+ - in: query
+ name: search[updater_name]
+ schema:
+ type: string
+ - in: query
+ name: search[updater_id]
+ schema:
+ type: number
+ - in: query
+ name: search[post_id]
+ schema:
+ type: number
+ - in: query
+ name: search[start_id]
+ schema:
+ type: number
+ - in: query
+ name: search[rating]
+ schema:
+ type: string
+ enum: *ratings
+ - in: query
+ name: search[rating_changed]
+ schema:
+ type: string
+ enum:
+ - e
+ - q
+ - s
+ - any
+ - in: query
+ name: search[parent_id]
+ schema:
+ type: number
+ - in: query
+ name: search[parent_id_changed]
+ schema:
+ type: boolean
+ - in: query
+ name: search[tags]
+ schema:
+ type: string
+ - in: query
+ name: search[tags_removed]
+ schema:
+ type: string
+ - in: query
+ name: search[tags_added]
+ schema:
+ type: string
+ - in: query
+ name: search[locked_tags]
+ schema:
+ type: string
+ - in: query
+ name: search[locked_tags_removed]
+ schema:
+ type: string
+ - in: query
+ name: search[locked_tags_added]
+ schema:
+ type: string
+ - in: query
+ name: search[reason]
+ schema:
+ type: string
+ - in: query
+ name: search[description]
+ schema:
+ type: string
+ - in: query
+ name: search[description_changed]
+ schema:
+ type: boolean
+ - in: query
+ name: search[source_changed]
+ schema:
+ type: boolean
+ - in: query
+ name: search[uploads]
+ schema:
+ type: string
+ enum:
+ - included
+ - excluded
+ - only
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/PostVersion"
+ # Post Votes
+ /posts/{id}/votes.json:
+ post:
+ summary: Create Post Vote
+ operationId: createPostVote
+ tags:
+ - Post Votes
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: string
+ - name: score
+ in: query
+ required: true
+ schema:
+ type: number
+ enum:
+ - -1
+ - 1
+ - name: no_unvote
+ in: query
+ schema:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - score
+ - up
+ - down
+ - our_score
+ properties:
+ score:
+ type: number
+ up:
+ type: number
+ down:
+ type: number
+ our_score:
+ type: number
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Post Vote
+ operationId: deletePostVote
+ tags:
+ - Post Votes
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /post_votes/lock.json:
+ post:
+ summary: Lock Post Vote
+ operationId: lockPostVote
+ tags:
+ - Post Votes
+ security:
+ - basicAuth: []
+ description: You must be Moderator+. Errors if ids is not provided.
+ parameters:
+ - name: ids
+ in: query
+ required: true
+ description: The IDs of the post votes, comma separated.
+ schema:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /post_votes/delete.json:
+ post:
+ summary: Delete Post Vote
+ operationId: deletePostVotes
+ tags:
+ - Post Votes
+ security:
+ - basicAuth: []
+ description: You must be Admin+. Errors if ids is not provided.
+ parameters:
+ - name: ids
+ in: query
+ required: true
+ description: The IDs of the post votes, comma separated.
+ schema:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ # Tags
+ /tags.json:
+ get:
+ summary: Search Tags
+ operationId: searchTags
+ tags:
+ - Tags
+ description: When no results are found, an object with an `tags` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - name
+ - date
+ - count
+ - similarity
+ - in: query
+ name: search[fuzzy_name_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[name_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[name]
+ schema:
+ type: string
+ - in: query
+ name: search[category]
+ schema:
+ type: number
+ description: Allows multiple, comma separated.
+ enum: *tag-categories
+ - in: query
+ name: search[hide_empty]
+ schema:
+ type: boolean
+ - in: query
+ name: search[has_wiki]
+ schema:
+ type: boolean
+ - in: query
+ name: search[has_artist]
+ schema:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/Tag"
+ - type: object
+ description: No Results
+ required:
+ - tags
+ properties:
+ tags:
+ type: array
+ maxItems: 0
+ /tags/{id}.json:
+ get:
+ summary: Get Tag
+ operationId: getTag
+ tags:
+ - Tags
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID or name of the tag.
+ schema:
+ type: ["number", "string"]
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Tag"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Tag
+ operationId: editTag
+ tags:
+ - Tags
+ security:
+ - basicAuth: []
+ description: Must be Admin+ if the tag is locked or post count is >100.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the tag.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ tag[category]:
+ type: number
+ enum: *tag-categories
+ tag[is_locked]:
+ description: Must be Admin+.
+ type: boolean
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /tags/{id}/correction.json:
+ post:
+ summary: Correct Tag
+ operationId: correctTag
+ tags:
+ - Tags
+ security:
+ - basicAuth: []
+ description: You must be Janitor+. `commit=Fix` must be set.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the tag.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - commit
+ properties:
+ commit:
+ type: string
+ description: If not set, nothing will happen.
+ responses:
+ 302:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /tags/preview.json:
+ get:
+ summary: Preview Tags
+ operationId: previewTags
+ tags:
+ - Tags
+ description: Note while this route does not require auth, without auth it requires a CSRF token. For that reason it has been marked as requiring auth.
+ security:
+ - basicAuth: []
+ parameters:
+ - name: tags
+ in: query
+ required: true
+ description: The tags to preview, space separated.
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/TagPreview"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Tag Type Versions
+ /tag_type_versions.json:
+ get:
+ summary: Search Tag Versions
+ operationId: searchTagVersions
+ tags:
+ - Tag Versions
+ description: When no results are found, an object with an `tag_type_versions` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[tag]
+ in: query
+ schema:
+ type: string
+ - name: search[user_id]
+ in: query
+ schema:
+ type: string
+ - name: search[user_name]
+ in: query
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/TagTypeVersion"
+ - type: object
+ description: No Results
+ required:
+ - tag_type_versions
+ properties:
+ tag_type_versions:
+ type: array
+ maxItems: 0
+ # Tag Aliases
+ /tag_aliases.json:
+ get:
+ summary: Search Tag Aliases
+ operationId: searchTagAliases
+ tags:
+ - Tag Aliases
+ description: When no results are found, an object with an `tag_aliases` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - created_at
+ - updated_at
+ - name
+ - tag_count
+ - in: query
+ name: search[name_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[antecedent_name]
+ schema:
+ type: string
+ - in: query
+ name: search[consequent_name]
+ schema:
+ type: string
+ - in: query
+ name: search[status]
+ schema:
+ type: string
+ enum: *tag-request-statuses
+ - in: query
+ name: search[antecedent_tag_category]
+ schema:
+ type: number
+ enum: *tag-categories
+ - in: query
+ name: search[consequent_tag_category]
+ schema:
+ type: number
+ enum: *tag-categories
+ - in: query
+ name: search[creator_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ schema:
+ type: string
+ - in: query
+ name: search[approver_id]
+ schema:
+ type: number
+ - in: query
+ name: search[approver_name]
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/TagAlias"
+ - type: object
+ description: No Results
+ required:
+ - tag_aliases
+ properties:
+ tag_aliases:
+ type: array
+ maxItems: 0
+ /tag_aliases/{id}.json:
+ get:
+ summary: Get Tag Alias
+ operationId: getTagAlias
+ tags:
+ - Tag Aliases
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the tag alias.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/TagAlias"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Tag Alias
+ operationId: editTagAlias
+ tags:
+ - Tag Aliases
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the tag alias.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ tag_alias[antecedent_name]:
+ type: string
+ tag_alias[consequent_name]:
+ type: string
+ tag_alias[forum_topic_id]:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Reject Tag Alias
+ operationId: rejectTagAlias
+ tags:
+ - Tag Aliases
+ description: You must be the creator of the request (if pending), or Admin+.
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the tag alias.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /tag_aliases/{id}/approve.json:
+ post:
+ summary: Approve Tag Alias
+ operationId: approveTagAlias
+ tags:
+ - Tag Aliases
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the tag alias.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /tag_alias_requests.json:
+ post:
+ summary: Create Tag Alias
+ operationId: createTagAlias
+ tags:
+ - Tag Aliases
+ security:
+ - basicAuth: []
+ description: Errors will result in a 406 with no information.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - tag_alias[antecedent_name]
+ - tag_alias[consequent_name]
+ - tag_alias[reason]
+ properties:
+ tag_alias[antecedent_name]:
+ type: string
+ tag_alias[consequent_name]:
+ type: string
+ tag_alias[reason]:
+ type: string
+ tag_alias[skip_forum]:
+ type: boolean
+ description: Must be Admin+.
+ responses:
+ 302:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 406:
+ description: Failure
+ # Tag Implications
+ /tag_implications.json:
+ get:
+ summary: Search Tag Implications
+ operationId: searchTagImplications
+ tags:
+ - Tag Implications
+ description: When no results are found, an object with an `tag_implications` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - created_at
+ - updated_at
+ - name
+ - tag_count
+ - in: query
+ name: search[name_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[antecedent_name]
+ schema:
+ type: string
+ - in: query
+ name: search[consequent_name]
+ schema:
+ type: string
+ - in: query
+ name: search[status]
+ schema:
+ type: string
+ enum: *tag-request-statuses
+ - in: query
+ name: search[antecedent_tag_category]
+ schema:
+ type: number
+ enum: *tag-categories
+ - in: query
+ name: search[consequent_tag_category]
+ schema:
+ type: number
+ enum: *tag-categories
+ - in: query
+ name: search[creator_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ schema:
+ type: string
+ - in: query
+ name: search[approver_id]
+ schema:
+ type: number
+ - in: query
+ name: search[approver_name]
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/TagImplication"
+ - type: object
+ description: No Results
+ required:
+ - tag_implications
+ properties:
+ tag_implications:
+ type: array
+ maxItems: 0
+ /tag_implications/{id}.json:
+ get:
+ summary: Get Tag Implication
+ operationId: getTagImplication
+ tags:
+ - Tag Implications
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the tag implication.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/TagImplication"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Tag Implication
+ operationId: editTagImplication
+ tags:
+ - Tag Implications
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the tag implication.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ tag_implication[antecedent_name]:
+ type: string
+ tag_implication[consequent_name]:
+ type: string
+ tag_implication[forum_topic_id]:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Reject Tag Implication
+ operationId: rejectTagImplication
+ tags:
+ - Tag Implications
+ description: You must be the creator of the request (if pending), or Admin+.
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the tag implication.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /tag_implications/{id}/approve.json:
+ post:
+ summary: Approve Tag Implication
+ operationId: approveTagImplication
+ tags:
+ - Tag Implications
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the tag implication.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /tag_implication_requests.json:
+ post:
+ summary: Create Tag Implication
+ operationId: createTagImplication
+ tags:
+ - Tag Implications
+ security:
+ - basicAuth: []
+ description: Errors will result in a 406 with no information.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - tag_implication[antecedent_name]
+ - tag_implication[consequent_name]
+ - tag_implication[reason]
+ properties:
+ tag_implication[antecedent_name]:
+ type: string
+ tag_implication[consequent_name]:
+ type: string
+ tag_implication[reason]:
+ type: string
+ tag_implication[skip_forum]:
+ type: boolean
+ description: Must be Admin+.
+ responses:
+ 302:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 406:
+ description: Failure
+ # Bulk Related Tags
+ /related_tag/bulk.json:
+ post:
+ summary: List Bulk Related Tags
+ operationId: listBulkRelatedTags
+ tags:
+ - Related Tags
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ query:
+ type: string
+ category_id:
+ type: number
+ enum: *tag-categories
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ examples:
+ - {"male":[{"name":"male","count":0,"category_id":0}]}
+ patternProperties:
+ "^.+$":
+ type: array
+ items:
+ $ref: "#/components/schemas/BulkRelatedTag"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ # Takedowns
+ /takedowns.json:
+ get:
+ summary: Search Takedowns
+ operationId: searchTakedowns
+ tags:
+ - Takedowns
+ description: When no results are found, an object with an `takedowns` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - $ref: "#/components/parameters/ip_addr"
+ - in: query
+ name: search[order]
+ schema:
+ type: string
+ description: Must Admin+ to use.
+ enum:
+ - id_asc
+ - id_desc
+ - status
+ - post_count
+ - in: query
+ name: search[status]
+ schema:
+ type: string
+ - in: query
+ name: search[source]
+ description: Must be Moderator+ to use.
+ schema:
+ type: string
+ - in: query
+ name: search[reason]
+ description: Must be Moderator+ to use.
+ schema:
+ type: string
+ - in: query
+ name: search[creator_id]
+ description: Must be Moderator+ to use.
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ description: Must be Moderator+ to use.
+ schema:
+ type: string
+ - in: query
+ name: search[reason_hidden]
+ description: Must be Moderator+ to use.
+ schema:
+ type: boolean
+ - in: query
+ name: search[instructions]
+ description: Must be Moderator+ to use.
+ schema:
+ type: string
+ - in: query
+ description: Must be Moderator+ to use.
+ name: search[post_id]
+ schema:
+ type: number
+ - in: query
+ description: Must be Moderator+ to use.
+ name: search[notes]
+ schema:
+ type: string
+ - in: query
+ description: Must be Admin+ to use.
+ name: search[email]
+ schema:
+ type: string
+ - in: query
+ name: search[vericode]
+ description: Must be Admin+ to use.
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/Takedown"
+ - type: object
+ description: No Results
+ required:
+ - takedowns
+ properties:
+ takedowns:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create Takedown
+ operationId: createTakedown
+ tags:
+ - Takedowns
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - takedown[email]
+ - takedown[reason]
+ properties:
+ takedown[email]:
+ type: string
+ takedown[source]:
+ type: string
+ takedown[instructions]:
+ type: string
+ takedown[reason]:
+ type: string
+ takedown[post_ids]:
+ type: array
+ description: takedown[post_ids][]=1&takedown[post_ids][]=2
+ items:
+ type: number
+ takedown[reason_hidden]:
+ type: boolean
+ takedown[notes]:
+ type: string
+ description: Must have the bd staff user flag to use.
+ takedown[del_post_ids]:
+ type: array
+ description: |
+ Must have the bd staff user flag to use.
+ takedown[del_post_ids][]=1&takedown[del_post_ids][]=2
+ items:
+ type: number
+ takedown[status]:
+ type: string
+ description: Must have the bd staff user flag to use.
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Takedown"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /takedowns/{id}.json:
+ get:
+ summary: Get Takedown
+ operationId: getTakedown
+ tags:
+ - Takedowns
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the takedown.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Takedown"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Takedown
+ operationId: editTakedown
+ tags:
+ - Takedowns
+ security:
+ - basicAuth: []
+ description: You must have the bd staff user flag.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the takedown.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ takedown[notes]:
+ type: string
+ takedown[reason_hidden]:
+ type: boolean
+ takedown_posts:
+ type: string
+ process_takedown:
+ type: boolean
+ description: If not truthy, the takedown will be denied.
+ delete_reason:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Takedown
+ operationId: deleteTakedown
+ tags:
+ - Takedowns
+ security:
+ - basicAuth: []
+ description: You must have the bd staff user flag.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the takedown.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /takedowns/{id}/add_by_ids.json:
+ post:
+ summary: Add Posts To Takedown By IDs
+ operationId: addPostsToTakedownByIds
+ tags:
+ - Takedowns
+ security:
+ - basicAuth: []
+ description: You must have the bd staff user flag.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the takedown.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - post_ids
+ properties:
+ post_ids:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - added_count
+ - added_post_ids
+ properties:
+ added_count:
+ type: number
+ added_post_ids:
+ type: array
+ items:
+ type: number
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /takedowns/{id}/add_by_tags.json:
+ post:
+ summary: Add Posts To Takedown By Tags
+ operationId: addPostsToTakedownByTags
+ tags:
+ - Takedowns
+ security:
+ - basicAuth: []
+ description: You must have the bd staff user flag.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the takedown.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - post_tags
+ properties:
+ post_tags:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - added_count
+ - added_post_ids
+ properties:
+ added_count:
+ type: number
+ added_post_ids:
+ type: array
+ items:
+ type: number
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /takedowns/{id}/count_matching_posts.json:
+ post:
+ summary: Count Matching Posts
+ operationId: countMatchingPosts
+ tags:
+ - Takedowns
+ security:
+ - basicAuth: []
+ description: You must have the bd staff user flag.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the takedown.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - post_tags
+ properties:
+ post_tags:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - matched_post_count
+ properties:
+ matched_post_count:
+ type: number
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /takedowns/{id}/remove_by_ids.json:
+ post:
+ summary: Remove Posts From Takedown By IDs
+ operationId: removePostsFromTakedownByIds
+ tags:
+ - Takedowns
+ security:
+ - basicAuth: []
+ description: You must have the bd staff user flag.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the takedown.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - post_ids
+ properties:
+ post_ids:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Tickets
+ /tickets.json:
+ get:
+ summary: Search Tickets
+ operationId: searchTickets
+ tags:
+ - Tickets
+ description: You must be Janitor+ to see tickets you did not create. When no results are found, an object with an `tickets` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - in: query
+ name: search[creator_name]
+ description: You must be Moderator+.
+ schema:
+ type: string
+ - in: query
+ name: search[creator_id]
+ description: You must be Moderator+ unless providing your own id.
+ schema:
+ type: number
+ - in: query
+ name: search[claimant_name]
+ description: You must be Moderator+.
+ schema:
+ type: string
+ - in: query
+ name: search[claimant_id]
+ description: You must be Moderator+.
+ schema:
+ type: number
+ - in: query
+ name: search[accused_name]
+ description: You must be Moderator+.
+ schema:
+ type: string
+ - in: query
+ name: search[accused_id]
+ description: You must be Moderator+.
+ schema:
+ type: number
+ - in: query
+ name: search[qtype]
+ schema:
+ type: string
+ enum: *ticket-types
+ - in: query
+ name: search[reason]
+ description: You must be Moderator+.
+ schema:
+ type: string
+ - in: query
+ name: search[status]
+ schema:
+ type: string
+ enum:
+ - pending
+ - partial
+ - approved
+ - pending_claimed
+ - pending_unclaimed
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/Ticket"
+ - type: object
+ description: No Results
+ required:
+ - tickets
+ properties:
+ tickets:
+ type: array
+ maxItems: 0
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ /tickets/{id}.json:
+ get:
+ summary: Get Ticket
+ operationId: getTicket
+ tags:
+ - Tickets
+ security:
+ - basicAuth: []
+ description: You must be Janitor+ to see tickets you did not create.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the ticket.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Ticket"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Ticket
+ operationId: editTicket
+ tags:
+ - Tickets
+ description: You must be Moderator+.
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the ticket.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - ticket[response]
+ properties:
+ ticket[status]:
+ type: string
+ enum:
+ - partial
+ - approved
+ ticket[response]:
+ type: string
+ ticket[record_type]:
+ type: string
+ enum: *warning-types
+ ticket[send_update_dmail]:
+ type: boolean
+ description: An update dmail will always be sent when the status is changed.
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /tickets/{id}/claim.json:
+ post:
+ summary: Claim Ticket
+ operationId: claimTicket
+ tags:
+ - Tickets
+ description: You must be Moderator+. Errors are quietly swallowed and shown as notices in html.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the ticket.
+ schema:
+ type: number
+ security:
+ - basicAuth: []
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Ticket"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /tickets/{id}/unclaim.json:
+ post:
+ summary: Unclaim Ticket
+ operationId: unclaimTicket
+ tags:
+ - Tickets
+ description: You must be Moderator+. Errors are quietly swallowed and shown as notices in html.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the ticket.
+ schema:
+ type: number
+ security:
+ - basicAuth: []
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Ticket"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Uploads
+ /uploads.json:
+ get:
+ summary: Search Uploads
+ operationId: searchUploads
+ tags:
+ - Uploads
+ description: You must be Janitor+. When no results are found, an object with an `uploads` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - in: query
+ name: search[uploader_id]
+ schema:
+ type: number
+ - in: query
+ name: search[uploader_name]
+ schema:
+ type: string
+ - in: query
+ name: search[source]
+ schema:
+ type: string
+ - in: query
+ name: search[source_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[rating]
+ schema:
+ type: string
+ enum: *ratings
+ - in: query
+ name: search[parent_id]
+ schema:
+ type: number
+ - in: query
+ name: search[post_id]
+ schema:
+ type: number
+ - in: query
+ name: search[has_post]
+ schema:
+ type: boolean
+ - in: query
+ name: search[post_tags_match]
+ schema:
+ type: string
+ - in: query
+ name: search[status]
+ schema:
+ type: string
+ description: |
+ Note: The "error" status will be proceeded by an error, ex: "error: RuntimeError - No file or source URL provided"
+ enum:
+ - completed
+ - processing
+ - pending
+ - error
+ - in: query
+ name: search[backtrace]
+ schema:
+ type: string
+ - in: query
+ name: search[tag_string]
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/Upload"
+ - type: object
+ description: No Results
+ required:
+ - uploads
+ properties:
+ uploads:
+ type: array
+ maxItems: 0
+ post:
+ summary: Upload Post
+ operationId: uploadPost
+ tags:
+ - Uploads
+ - Posts
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ required:
+ - upload[tag_string]
+ - upload[rating]
+ properties:
+ upload[file]:
+ type: string
+ format: binary
+ description: Mutually exclusive with direct_url.
+ upload[direct_url]:
+ type: string
+ description: Mutually exclusive with file.
+ upload[source]:
+ type: string
+ upload[tag_string]:
+ type: string
+ upload[rating]:
+ type: string
+ enum: *ratings
+ upload[parent_id]:
+ type: number
+ upload[description]:
+ type: string
+ upload[as_pending]:
+ type: boolean
+ description: Must have the "Unrestricted Uploads" permission.
+ upload[locked_rating]:
+ type: boolean
+ description: Must be Privileged+ to use.
+ upload[locked_tags]:
+ type: string
+ description: Must be Admin+ to use.
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - success
+ - location
+ - post_id
+ properties:
+ success:
+ type: boolean
+ enum:
+ - true
+ location:
+ type: string
+ post_id:
+ type: number
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 412:
+ $ref: "#/components/responses/MessageError"
+ # Upload Whitelists
+ /upload_whitelists.json:
+ get:
+ summary: Search Upload Whitelists
+ operationId: searchUploadWhitelists
+ tags:
+ - Upload Whitelists
+ description: When no results are found, an object with an `upload_whitelists` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - pattern
+ - updated_at
+ - created_at
+ - in: query
+ name: search[pattern]
+ schema:
+ type: string
+ - in: query
+ name: search[note]
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/UploadWhitelist"
+ - type: object
+ description: No Results
+ required:
+ - upload_whitelists
+ properties:
+ upload_whitelists:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create Upload Whitelist
+ operationId: createUploadWhitelist
+ tags:
+ - Upload Whitelists
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - upload_whitelist[allowed]
+ - upload_whitelist[pattern]
+ properties:
+ upload_whitelist[allowed]:
+ type: string
+ upload_whitelist[pattern]:
+ type: string
+ upload_whitelist[reason]:
+ type: string
+ upload_whitelist[note]:
+ type: string
+ upload_whitelist[hidden]:
+ type: boolean
+ /upload_whitelists/{id}.json:
+ patch:
+ summary: Edit Upload Whitelist
+ operationId: editUploadWhitelist
+ tags:
+ - Upload Whitelists
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the upload whitelist entry.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ upload_whitelist[allowed]:
+ type: string
+ upload_whitelist[pattern]:
+ type: string
+ upload_whitelist[reason]:
+ type: string
+ upload_whitelist[note]:
+ type: string
+ upload_whitelist[hidden]:
+ type: boolean
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Upload Whitelist
+ operationId: deleteUploadWhitelist
+ tags:
+ - Upload Whitelists
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the upload whitelist.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /upload_whitelists/{id}/is_allowed.json:
+ get:
+ summary: Check If URL Is Allowed
+ operationId: checkIfUrlIsAllowed
+ tags:
+ - Upload Whitelists
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the upload whitelist.
+ schema:
+ type: number
+ - name: url
+ in: query
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - url
+ - domain
+ - is_allowed
+ - reason
+ properties:
+ url:
+ type: string
+ domain:
+ type: string
+ is_allowed:
+ type: boolean
+ reason:
+ type: string
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ # Users
+ /users.json:
+ get:
+ summary: Search Users
+ operationId: searchUsers
+ tags:
+ - Users
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - $ref: "#/components/parameters/ip_addr"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - name
+ - post_upload_count
+ - note_count
+ - post_update_count
+ - in: query
+ name: search[name_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[about_me]
+ schema:
+ type: string
+ - in: query
+ name: search[avatar_id]
+ schema:
+ type: number
+ - in: query
+ name: search[level]
+ schema:
+ type: number
+ - in: query
+ name: search[min_level]
+ schema:
+ type: number
+ - in: query
+ name: search[max_level]
+ schema:
+ type: number
+ - in: query
+ name: search[can_upload_free]
+ schema:
+ type: boolean
+ - in: query
+ name: search[can_approve_posts]
+ schema:
+ type: boolean
+ - in: query
+ name: search[email_matches]
+ description: You must be Admin+.
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ anyOf:
+ - $ref: "#/components/schemas/User"
+ - $ref: "#/components/schemas/FullUser"
+ description: Extra properties included for the current user.
+ /users/{id}.json:
+ get:
+ summary: Get User
+ operationId: getUser
+ tags:
+ - Users
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the user.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - $ref: "#/components/schemas/User"
+ - $ref: "#/components/schemas/FullCurrentUser"
+ description: Extra properties included for the current user.
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Current User
+ operationId: editCurrentUser
+ tags:
+ - Users
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the user. The actual value is ignored, but something must be supplied.
+ schema:
+ type: number
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ user[comment_threshold]:
+ type: number
+ user[default_image_size]:
+ type: string
+ enum:
+ - large
+ - fit
+ - fitv
+ - original
+ user[favorite_tags]:
+ type: string
+ user[blacklisted_tags]:
+ type: string
+ user[time_zone]:
+ type: string
+ description: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+ user[per_page]:
+ type: number
+ user[custom_style]:
+ type: string
+ user[description_collapsed_initially]:
+ type: boolean
+ user[hide_comments]:
+ type: boolean
+ user[receive_email_notifications]:
+ type: boolean
+ user[enable_keyboard_navigation]:
+ type: boolean
+ user[enable_privacy_mode]:
+ type: boolean
+ user[disable_user_dmails]:
+ type: boolean
+ user[blacklist_users]:
+ type: boolean
+ user[show_post_statistics]:
+ type: boolean
+ user[style_usernames]:
+ type: boolean
+ user[show_hidden_comments]:
+ type: boolean
+ user[enable_autocomplete]:
+ type: boolean
+ user[disable_cropped_thumbnails]:
+ type: boolean
+ user[enable_safe_mode]:
+ type: boolean
+ user[disable_responsive_mode]:
+ type: boolean
+ user[dmail_filter_attributes][id]:
+ type: number
+ user[dmail_filter_attributes][words]:
+ type: string
+ user[profile_about]:
+ type: string
+ user[profile_artinfo]:
+ type: string
+ user[avatar_id]:
+ type: number
+ user[enable_compact_uploader]:
+ type: boolean
+ description: You must have uploaded at least 10 posts.
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /users/upload_limit.json:
+ get:
+ summary: Get Current User
+ operationId: getCurrentUser
+ tags:
+ - Users
+ security:
+ - basicAuth: []
+ description: |
+ This is a crude but effective way to get the currently authenticated user without scraping HTML.
+ Note that this route does not include some properties included in the show action.
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/CurrentUser"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ /maintenance/user/count_fixes.json:
+ post:
+ summary: Fix User Counts
+ operationId: fixUserCounts
+ tags:
+ - Users
+ security:
+ - basicAuth: []
+ responses:
+ 302:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ /maintenance/user/dmail_filter.json:
+ patch:
+ summary: Update User Dmail Filter
+ operationId: updateUserDmailFilter
+ tags:
+ - DMails
+ parameters:
+ - name: dmail_id
+ in: query
+ required: true
+ description: Due to the odd way this route works, a dmail is REQUIRED to edit your dmail filter. You must be the owner of the dmail.
+ schema:
+ type: number
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - dmail_filter[words]
+ properties:
+ dmail_filter[words]:
+ type: string
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ # User Feedbacks
+ /user_feedbacks.json:
+ get:
+ summary: Search User Feedbacks
+ operationId: searchUserFeedbacks
+ tags:
+ - User Feedbacks
+ description: When no results are found, an object with an `user_feedbacks` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - in: query
+ name: search[deleted]
+ description: You must be Moderator+.
+ schema:
+ type: string
+ enum:
+ - included
+ - excluded
+ - only
+ - in: query
+ name: search[body_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[user_id]
+ schema:
+ type: number
+ - in: query
+ name: search[user_name]
+ schema:
+ type: string
+ - in: query
+ name: search[creator_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ schema:
+ type: string
+ - in: query
+ name: search[category]
+ schema:
+ type: string
+ enum: *feedback-categories
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/UserFeedback"
+ - type: object
+ description: No Results
+ required:
+ - user_feedbacks
+ properties:
+ user_feedbacks:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create User Feedback
+ operationId: createUserFeedback
+ tags:
+ - User Feedbacks
+ security:
+ - basicAuth: []
+ description: You must be Moderator+.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - user_feedback[body]
+ - user_feedback[category]
+ properties:
+ user_feedback[user_id]:
+ type: number
+ user_feedback[user_name]:
+ type: string
+ user_feedback[body]:
+ type: string
+ user_feedback[category]:
+ type: string
+ enum: *feedback-categories
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UserFeedback"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /user_feedbacks/{id}.json:
+ get:
+ summary: Get User Feedback
+ operationId: getUserFeedback
+ tags:
+ - User Feedbacks
+ description: You must be Moderator+ if the feedback is deleted.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the feedback.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UserFeedback"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit User Feedback
+ operationId: editUserFeedback
+ tags:
+ - User Feedbacks
+ security:
+ - basicAuth: []
+ description: You must be Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the feedback.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ user_feedback[body]:
+ type: string
+ user_feedback[category]:
+ type: string
+ enum: *feedback-categories
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Destroy User Feedback
+ operationId: destroyUserFeedback
+ tags:
+ - User Feedbacks
+ security:
+ - basicAuth: []
+ description: You must be Admin+, or the creator and Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the feedback.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /user_feedbacks/{id}/delete.json:
+ put:
+ summary: Delete User Feedback
+ operationId: deleteUserFeedback
+ tags:
+ - User Feedbacks
+ security:
+ - basicAuth: []
+ description: You must be Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the feedback.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /user_feedbacks/{id}/undelete.json:
+ put:
+ summary: Undelete User Feedback
+ operationId: undeleteUserFeedback
+ tags:
+ - User Feedbacks
+ security:
+ - basicAuth: []
+ description: You must be Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the feedback.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # User Name Change Requests
+ /user_name_change_requests.json:
+ get:
+ summary: Search User Name Change Requests
+ operationId: searchUserNameChangeRequests
+ tags:
+ - User Name Change Requests
+ security:
+ - basicAuth: []
+ description: You must be Moderator+. When no results are found, an object with an `user_name_change_requests` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - in: query
+ name: search[current_id]
+ schema:
+ type: number
+ - in: query
+ name: search[current_name]
+ schema:
+ type: string
+ - in: query
+ name: search[original_name]
+ schema:
+ type: string
+ - in: query
+ name: search[desired_name]
+ schema:
+ type: string
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/UserNameChangeRequest"
+ - type: object
+ description: No Results
+ required:
+ - user_name_change_requests
+ properties:
+ user_name_change_requests:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create User Name Change Request
+ operationId: createUserNameChangeRequest
+ tags:
+ - User Name Change Requests
+ security:
+ - basicAuth: []
+ description: You must be Moderator+.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - user_name_change_request[desired_name]
+ properties:
+ user_name_change_request[desired_name]:
+ type: string
+ user_name_change_request[change_reason]:
+ type: string
+ responses:
+ 302:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /user_name_change_requests/{id}.json:
+ get:
+ summary: Get User Name Change Request
+ operationId: getUserNameChangeRequest
+ tags:
+ - User Name Change Requests
+ security:
+ - basicAuth: []
+ description: You must be the creator of the request or Moderator+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the name change request.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UserNameChangeRequest"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Wiki Pages
+ /wiki_pages.json:
+ get:
+ summary: Search Wiki Pages
+ operationId: searchWikiPages
+ tags:
+ - Wiki Pages
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - title
+ - post_count
+ - in: query
+ name: search[title]
+ schema:
+ type: string
+ - in: query
+ name: search[title_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[body_matches]
+ schema:
+ type: string
+ - in: query
+ name: search[other_names_match]
+ schema:
+ type: string
+ - in: query
+ name: search[creator_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ schema:
+ type: string
+ - in: query
+ name: search[parent]
+ schema:
+ type: string
+ - in: query
+ name: search[other_names_present]
+ schema:
+ type: boolean
+ - in: query
+ name: search[is_locked]
+ schema:
+ type: boolean
+ - in: query
+ name: search[is_deleted]
+ schema:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/WikiPage"
+ post:
+ summary: Create Wiki Page
+ operationId: createWikiPage
+ tags:
+ - Wiki Pages
+ security:
+ - basicAuth: []
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - wiki_page[title]
+ - wiki_page[body]
+ properties:
+ wiki_page[title]:
+ type: string
+ wiki_page[body]:
+ type: string
+ wiki_page[edit_reason]:
+ type: string
+ wiki_page[parent]:
+ type: string
+ description: Must be Privileged+ to use.
+ wiki_page[is_locked]:
+ type: boolean
+ description: Must be Janitor+ to use.
+ wiki_page[is_deleted]:
+ type: boolean
+ description: Must be Janitor+ to use.
+ wiki_page[skip_secondary_validations]:
+ type: boolean
+ description: Must be Janitor+ to use.
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/WikiPage"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /wiki_pages/{id}.json:
+ get:
+ summary: Get Wiki Page
+ operationId: getWikiPage
+ tags:
+ - Wiki Pages
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID or name of the wiki page.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/WikiPage"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ patch:
+ summary: Edit Wiki Page
+ operationId: editWikiPage
+ tags:
+ - Wiki Pages
+ security:
+ - basicAuth: []
+ description: You must be Janitor+ if the wiki page is locked.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the wiki page.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ wiki_page[body]:
+ type: string
+ wiki_page[edit_reason]:
+ type: string
+ wiki_page[parent]:
+ type: string
+ description: Must be Privileged+ to use.
+ wiki_page[title]:
+ type: string
+ description: Must be Janitor+ to use.
+ wiki_page[is_locked]:
+ type: boolean
+ description: Must be Janitor+ to use.
+ wiki_page[is_deleted]:
+ type: boolean
+ description: Must be Janitor+ to use.
+ wiki_page[skip_secondary_validations]:
+ type: boolean
+ description: Must be Janitor+ to use.
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ delete:
+ summary: Delete Wiki Page
+ operationId: deleteWikiPage
+ tags:
+ - Wiki Pages
+ security:
+ - basicAuth: []
+ description: You must be Admin+
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the wiki page.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /wiki_page/{id}/revert.json:
+ put:
+ summary: Revert Wiki Page
+ operationId: revertWikiPage
+ tags:
+ - Wiki Pages
+ security:
+ - basicAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the wiki page.
+ schema:
+ type: number
+ - name: version_id
+ in: query
+ required: true
+ description: The version ID to revert to.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ # Wiki Page Versions
+ /wiki_page_versions.json:
+ get:
+ summary: Search Wiki Page Versions
+ operationId: searchWikiPageVersions
+ tags:
+ - Wiki Page Versions
+ description: When no results are found, an object with an `wiki_page_versions` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - $ref: "#/components/parameters/ip_addr"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - in: query
+ name: search[updater_id]
+ schema:
+ type: number
+ - in: query
+ name: search[updater_name]
+ schema:
+ type: string
+ - in: query
+ name: search[wiki_page_id]
+ schema:
+ type: number
+ - in: query
+ name: search[title]
+ schema:
+ type: string
+ - in: query
+ name: search[body]
+ schema:
+ type: string
+ - in: query
+ name: search[is_locked]
+ schema:
+ type: boolean
+ - in: query
+ name: search[is_deleted]
+ schema:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/WikiPageVersion"
+ - type: object
+ description: No Results
+ required:
+ - wiki_page_versions
+ properties:
+ wiki_page_versions:
+ type: array
+ maxItems: 0
+ /wiki_page_versions/{id}.json:
+ get:
+ summary: Get Wiki Page Version
+ operationId: getWikiPageVersion
+ tags:
+ - Wiki Page Versions
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the wiki page version.
+ schema:
+ type: number
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/WikiPageVersion"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Admin Users
+ /admin/users/alt_list.json:
+ get:
+ summary: Get Alt List
+ operationId: getAltList
+ tags:
+ - Admin Users
+ security:
+ - basicAuth: []
+ description: You must be Admin+.
+ parameters:
+ - name: page
+ in: query
+ description: The page number of results to get. Between 1 and 9999.
+ schema:
+ type: number
+ minimum: 1
+ maximum: 9999
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ oneOf:
+ - type: number
+ description: ID of concerned user.
+ - type: array
+ items:
+ type: number
+ description: ID of suspected alt.
+ examples:
+ - [[1,[2]],[2,[1]]]
+ /admins/users/{id}.json:
+ patch:
+ summary: Admin Edit User
+ operationId: adminEditUser
+ tags:
+ - Admin Users
+ security:
+ - basicAuth: []
+ description: You must be Admin+. If editing an Admin+, you must be Owner+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the user.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ user[verified]:
+ type: boolean
+ description: Must have the bd staff user flag to use.
+ user[level]:
+ type: number
+ description: Must have the bd staff user flag to promote to Admin+.
+ user[name]:
+ type: string
+ user[profile_about]:
+ type: string
+ user[profile_artinfo]:
+ type: string
+ user[base_upload_limit]:
+ type: number
+ user[enable_privacy_mode]:
+ type: boolean
+ user[email]:
+ type: string
+ description: Must have the bd staff user flag to use.
+ user[can_approve_posts]:
+ type: boolean
+ user[can_upload_free]:
+ type: boolean
+ user[no_flagging]:
+ type: boolean
+ user[replacements_beta]:
+ type: boolean
+ responses:
+ 204:
+ description: Success
+ 400:
+ $ref: "#/components/responses/MessageError"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ # Moderator Posts
+ /moderator/post/posts/{id}/delete.json:
+ post:
+ summary: Delete Post
+ operationId: deletePost
+ tags:
+ - Posts
+ security:
+ - basicAuth: []
+ description: You must have the "Approve Posts" permission. `commit=Delete` must be set.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ reason:
+ type: string
+ description: If the post does not have an active flag, this is required.
+ move_favorites:
+ type: boolean
+ description: Move favorites to parent.
+ copy_sources:
+ type: boolean
+ description: Copy sources to parent.
+ copy_tags:
+ type: boolean
+ description: Copy tags to parent.
+ commit:
+ type: string
+ description: If not set, nothing will happen.
+ enum:
+ - Delete
+
+ responses:
+ 302:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /moderator/post/posts/{id}/undelete.json:
+ post:
+ summary: Undelete Post
+ operationId: undeletePost
+ tags:
+ - Posts
+ security:
+ - basicAuth: []
+ description: You must have the "Approve Posts" permission.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - post
+ properties:
+ post:
+ $ref: "#/components/schemas/Post"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /moderator/post/posts/{id}/regenerate_thumbnails.json:
+ post:
+ summary: Regenerate Post Thumbnails
+ operationId: regeneratePostThumbnails
+ tags:
+ - Posts
+ security:
+ - basicAuth: []
+ description: You must be Janitor+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: number
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - post
+ properties:
+ post:
+ $ref: "#/components/schemas/Post"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /moderator/post/posts/{id}/regenerate_videos.json:
+ post:
+ summary: Regenerate Post Videos
+ operationId: regeneratePostVideos
+ tags:
+ - Posts
+ security:
+ - basicAuth: []
+ description: You must be Janitor+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: number
+ responses:
+ 204:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /moderator/post/posts/{id}/expunge.json:
+ post:
+ summary: Expunge Post
+ operationId: expungePost
+ tags:
+ - Posts
+ security:
+ - basicAuth: []
+ description: You must have the "Approve Posts" permission and be Admin+.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ reason:
+ type: string
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - post
+ properties:
+ post:
+ $ref: "#/components/schemas/Post"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
+ /moderator/post/posts/{id}/move_favorites.json:
+ post:
+ summary: Move Post Favorites
+ operationId: movePostFavorites
+ tags:
+ - Posts
+ security:
+ - basicAuth: []
+ description: You must have the "Approve Posts" permission. `commit=Submit`` must be set.
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The ID of the post.
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - commit
+ properties:
+ commit:
+ type: string
+ description: If not set, nothing will happen.
+ enum:
+ - Submit
+ responses:
+ 302:
+ description: Success
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ /moderator/post/approval.json:
+ post:
+ summary: Approve Post
+ operationId: approvePost
+ tags:
+ - Posts
+ - Post Approvals
+ security:
+ - basicAuth: []
+ description: You must have the "Approve Posts" permission.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - post_id
+ properties:
+ post_id:
+ type: number
+ responses:
+ 201:
+ description: Success
+ 204:
+ description: Failure
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ delete:
+ summary: Unapprove Post
+ operationId: unapprovePost
+ tags:
+ - Posts
+ - Post Approvals
+ security:
+ - basicAuth: []
+ description: You must have the "Approve Posts" permission. The response does not differ for success or failure.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - post_id
+ properties:
+ post_id:
+ type: number
+ responses:
+ 204:
+ description: Success/Failure
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 404:
+ $ref: "#/components/responses/NotFound"
+ # Post Disapprovals
+ /moderator/post/disapprovals.json:
+ get:
+ summary: Search Post Disapprovals
+ operationId: searchPostDisapprovals
+ tags:
+ - Post Disapprovals
+ security:
+ - basicAuth: []
+ description: You must have the "Approve Posts" permission When no results are found, an object with a `post_disapprovals` key is returned.
+ parameters:
+ - $ref: "#/components/parameters/limit"
+ - $ref: "#/components/parameters/page"
+ - $ref: "#/components/parameters/id"
+ - name: search[order]
+ in: query
+ schema:
+ type: string
+ enum:
+ - id_asc
+ - id_desc
+ - post_id
+ - post_id_desc
+ - in: query
+ name: search[creator_id]
+ schema:
+ type: number
+ - in: query
+ name: search[creator_name]
+ schema:
+ type: string
+ - in: query
+ name: search[post_id]
+ schema:
+ type: number
+ - in: query
+ name: search[message]
+ schema:
+ type: string
+ - in: query
+ name: search[post_tags_match]
+ schema:
+ type: string
+ - in: query
+ name: search[reason]
+ schema:
+ type: string
+ - in: query
+ name: search[has_message]
+ schema:
+ type: boolean
+ responses:
+ 200:
+ description: Success
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - type: array
+ items:
+ $ref: "#/components/schemas/PostDisapproval"
+ - type: object
+ description: No Results
+ required:
+ - post_disapprovals
+ properties:
+ post_disapprovals:
+ type: array
+ maxItems: 0
+ post:
+ summary: Create Post Disapproval
+ operationId: createPostDisapproval
+ tags:
+ - Post Disapprovals
+ security:
+ - basicAuth: []
+ description: You must have the "Approve Posts" permission.
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ required:
+ - post_disapproval[post_id]
+ - post_disapproval[reason]
+ properties:
+ post_disapproval[post_id]:
+ type: number
+ post_disapproval[reason]:
+ type: string
+ enum:
+ - borderline_quality
+ - borderline_relevancy
+ - other
+ post_disapproval[message]:
+ type: string
+ responses:
+ 201:
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/PostDisapproval"
+ 403:
+ $ref: "#/components/responses/AccessDenied"
+ 422:
+ $ref: "#/components/responses/ExpectedError"
diff --git a/src/s3.go b/src/s3.go
index 7ed18ec..2ffa8a7 100644
--- a/src/s3.go
+++ b/src/s3.go
@@ -49,6 +49,7 @@ func NewS3Service(ctx context.Context, region, endpoint, accessKey, secretKey, b
uploader := manager.NewUploader(s3Client, func(u *manager.Uploader) {
u.PartSize = 10 * 1024 * 1024 // 10MB Parts
+ u.Concurrency = 999999 // give me all the power! (i mean, threads)
})
downloader := manager.NewDownloader(s3Client, func(d *manager.Downloader) {
diff --git a/update_openapi.sh b/update_openapi.sh
new file mode 100644
index 0000000..cd9d704
--- /dev/null
+++ b/update_openapi.sh
@@ -0,0 +1,5 @@
+echo "Deleting old routes"
+rm ./src/openapi/e621.yaml
+
+echo "Updating e621 routes"
+curl -o ./src/openapi/e621.yaml https://raw.githubusercontent.com/DonovanDMC/E621OpenAPI/master/openapi.yaml