{
  "$id": "configuration",
  "title": "Configuration",
  "description": "Configuration for Kemono.",
  "type": "object",
  "additionalProperties": false,
  "required": ["site", "development_mode", "automatic_migrations", "webserver"],
  "properties": {
    "site": {
      "description": "Deprecated in favour of `#/webserver/site`.",
      "$comment": "`$deprecated` keyword was in introduced in draft `2019-09`, so just using `description` field instead.",
      "type": "string"
    },
    "development_mode": {
      "type": "boolean"
    },
    "automatic_migrations": {
      "type": "boolean"
    },
    "sentry_dsn": {
      "type": "string"
    },
    "sentry_dsn_js": {
      "type": "string"
    },
    "open_telemetry_endpoint": {
      "type": "null"
    },
    "ban_url": {
      "description": "Kemono3 `BAN` URL prefix for cache purging.",
      "anyOf": [
        { "type": "string" },
        { "type": "array", "items": { "type": "string" } }
      ]
    },
    "enable_notifications": {
      "type": "boolean"
    },
    "cache_ttl_for_recent_posts": {
      "type": "integer"
    },
    "webserver": {
      "$ref": "#/definitions/webserver"
    },
    "database": {
      "$ref": "#/definitions/database"
    },
    "redis": {
      "$ref": "#/definitions/redis"
    },
    "archive_server": {
      "$ref": "#/definitions/archive-server"
    },
    "filehaus": {
      "$ref": "#/definitions/filehaus"
    }
  },
  "definitions": {
    "webserver": {
      "description": "Configuration for the frontend server.",
      "type": "object",
      "additionalProperties": false,
      "required": ["secret", "port", "ui"],
      "properties": {
        "secret": {
          "description": "Secret key used to encrypt sessions.",
          "type": "string"
        },
        "workers": {
          "description": "Amount of workers to run at once.",
          "type": "integer"
        },
        "harakiri": {
          "$comment": "This one isn't used by server code.",
          "type": "integer"
        },
        "threads": {
          "description": "Amount of thread to run at once.",
          "type": "integer"
        },
        "ip_security": {
          "description": "If you've dealt with how the trust of forwarding IPs works upstream, flip this off.",
          "type": "boolean"
        },
        "country_header_key": {
          "description": "Header for user country iso, generater by DDOS-GUARD.",
          "anyOf": [{ "type": "string" }, { "const": "DDG-Connecting-Country" }]
        },
        "uwsgi_options": {
          "$ref": "#/definitions/uwsgi-options"
        },
        "logging": {
          "description": "Logging mode.",
          "enum": ["DEBUG", "ERROR"]
        },
        "site": {
          "description": "The URL at which the site is publicly accessible.",
          "type": "string"
        },
        "static_folder": {
          "description": "The location of the resources that will be served.",
          "type": "string"
        },
        "template_folder": {
          "type": "string"
        },
        "jinja_bytecode_cache_path": {
          "type": "null"
        },
        "max_full_text_search_input_len": {
          "type": "integer"
        },
        "table_sample_bernoulli_sample_size": {
          "type": "number"
        },
        "extra_pages_to_load_on_posts": {
          "type": "integer"
        },
        "pages_in_popular": {
          "type": "integer"
        },
        "earliest_date_for_popular": {
          "type": "string"
        },
        "use_redis_by_lock_default_on_queries": {
          "type": "boolean"
        },
        "api": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "creators_location": {
              "type": "string"
            }
          }
        },
        "base_url": {
          "type": "string"
        },
        "port": {
          "description": "The port the site will be served on.",
          "type": "integer"
        },
        "ui": {
          "$ref": "#/definitions/ui"
        }
      }
    },
    "ui": {
      "description": "Interface preferences and customization options.",
      "type": "object",
      "additionalProperties": false,
      "required": ["home", "config"],
      "properties": {
        "home": {
          "$ref": "#/definitions/ui-home"
        },
        "config": {
          "$ref": "#/definitions/ui-config"
        },
        "fileservers": {
          "type": "array",
          "items": {
            "type": "array",
            "minItems": 2,
            "maxItems": 2,
            "items": [
              { "type": "string" },
              { "anyOf": [{ "type": "integer" }, { "type": "string" }] }
            ]
          }
        },
        "sidebar": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "disable_filehaus": {
              "type": "boolean"
            },
            "disable_faq": {
              "type": "boolean"
            },
            "disable_dms": {
              "type": "boolean"
            }
          }
        },
        "sidebar_items": {
          "description": "Add custom links to the bottom of the sidebar.",
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["text"],
            "properties": {
              "header": { "type": "boolean" },
              "text": { "type": "string" },
              "class_name": { "type": "string" },
              "link": { "type": "string" },
              "is_external": { "type": "boolean" },
              "color": { "type": "string" }
            }
          }
        },
        "footer_items": {
          "description": "Add custom HTML elements to the footer.",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "banner": {
          "description": "Banner HTML. Each entry should be Base64-encoded.",
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "global": {
              "description": "B64-encoded html fragment.",
              "type": "string"
            },
            "welcome": {
              "description": "B64-encoded html fragment.",
              "type": "string"
            }
          }
        },
        "ads": {
          "$ref": "#/definitions/ui-ads"
        },
        "matomo": {
          "$ref": "#/definitions/matomo"
        },
        "video_extensions": {
          "description": "File extensions recognized as (browser-friendly) video. Will automatically be embedded in post pages.",
          "type": "array",
          "uniqueItems": true,
          "items": {
            "enum": [".mp4", ".webm", ".m4v", ".3gp", ".mov"]
          }
        },
        "files_url_prepend": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "icons_base_url": {
              "type": "string"
            },
            "banners_base_url": {
              "type": "string"
            },
            "thumbnails_base_url": {
              "type": "string"
            }
          }
        }
      }
    },
    "ui-home": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "site_name": { "type": "string" },
        "welcome_credits": {
          "description": "B64-encoded html fragment.",
          "type": "string"
        },
        "home_background_image": {
          "description": "A path to background image on server.",
          "type": "string"
        },
        "logo_path": {
          "type": "string"
        },
        "mascot_path": {
          "type": "string"
        },
        "announcements": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "title": { "type": "string" },
              "date": { "type": "string" },
              "content": { "type": "string" }
            }
          }
        }
      }
    },
    "ui-config": {
      "type": "object",
      "additionalProperties": false,
      "required": ["paysite_list"],
      "properties": {
        "paysite_list": {
          "type": "array",
          "uniqueItems": true,
          "items": {
            "enum": [
              "patreon",
              "fanbox",
              "afdian",
              "discord",
              "fantia",
              "boosty",
              "gumroad",
              "subscribestar",
              "dlsite",
              "candfans",
              "fansly",
              "onlyfans"
            ]
          }
        },
        "artists_or_creators": {
          "type": "string"
        }
      }
    },
    "ui-ads": {
      "description": "Ads preferences. Each spot should be Base64-encoded.",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "header": {
          "description": "Base64-encoded html fragment.",
          "type": "string"
        },
        "middle": {
          "description": "Base64-encoded html fragment.",
          "type": "string"
        },
        "footer": {
          "description": "Base64-encoded html fragment.",
          "type": "string"
        },
        "slider": {
          "description": "Base64-encoded html fragment.",
          "type": "string"
        },
        "video": {
          "description": "Base64-encoded JSON list of objects.\nSee https://docs.fluidplayer.com/docs/configuration/ads/#adlist .",
          "type": "string"
        }
      }
    },
    "matomo": {
      "description": "Matomo preferences.",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "enabled": { "type": "boolean" },
        "tracking_domain": {
          "type": "string"
        },
        "tracking_code": {
          "type": "string"
        },
        "site_id": {
          "type": "integer"
        },
        "plain_code": {
          "description": "Base64-encoded html fragment.",
          "type": "string"
        }
      }
    },
    "database": {
      "description": "Database configuration.",
      "type": "object",
      "additionalProperties": false,
      "required": ["host", "user", "password", "database"],
      "properties": {
        "host": { "type": "string" },
        "port": { "type": "integer" },
        "user": { "type": "string" },
        "password": { "type": "string" },
        "database": { "type": "string" },
        "application_name": { "type": "string" }
      }
    },
    "redis": {
      "type": "object",
      "additionalProperties": false,
      "required": ["defaults"],
      "properties": {
        "defaults": { "$ref": "#/definitions/redis-defaults" },
        "compression": { "type": "string" },
        "default_ttl": { "type": "string" },
        "node_options": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "host": {
              "type": "string"
            },
            "port": {
              "type": "integer"
            },
            "db": {
              "type": "integer"
            },
            "password": {
              "type": "string"
            }
          }
        },
        "nodes": {
          "anyOf": [
            {
              "type": "object",
              "additionalProperties": { "type": "string" }
            },
            {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "port": {
                    "type": "integer"
                  },
                  "db": {
                    "type": "integer"
                  }
                }
              }
            }
          ]
        },
        "keyspaces": {
          "type": "object",
          "propertyNames": {
            "$ref": "#/definitions/redis-keyspaces"
          },
          "additionalProperties": {
            "type": "integer"
          }
        }
      }
    },
    "redis-defaults": {
      "type": "object",
      "additionalProperties": false,
      "required": ["host"],
      "properties": {
        "host": {
          "type": "string"
        },
        "port": {
          "type": "integer"
        },
        "db": {
          "type": "integer"
        }
      }
    },
    "redis-keyspaces": {
      "enum": [
        "account",
        "saved_key_import_ids",
        "saved_keys",
        "random_artist_keys",
        "top_artists",
        "artists_by_update_time",
        "artists_faved",
        "artists_faved_count",
        "artists_recently_faved_count",
        "top_artists_recently",
        "non_discord_artist_keys",
        "non_discord_artists",
        "artists_by_service",
        "artist",
        "artist_post_count",
        "artist_post_offset",
        "artist_last_updated",
        "artist_favorited",
        "dms",
        "all_dms",
        "all_dms_count",
        "all_dms_by_query",
        "all_dms_by_query_count",
        "dms_count",
        "unapproved_dms",
        "favorite_artists",
        "favorite_posts",
        "notifications_for_account",
        "random_post_keys",
        "post",
        "next_post",
        "previous_post",
        "posts_incomplete_rewards",
        "post_favorited",
        "posts_by_artist",
        "posts_by_artists",
        "posts_by_favorited_artists",
        "comments",
        "artist_posts_offset",
        "is_post_flagged",
        "importer_logs",
        "ratelimit",
        "all_posts",
        "all_post_keys",
        "all_shares",
        "all_shares_count",
        "all_posts_for_query",
        "global_post_count",
        "global_post_count_for_query",
        "lock",
        "lock-signal",
        "imports",
        "share_files",
        "account_notifications",
        "new_notifications",
        "share",
        "artist_shares",
        "post_revisions",
        "post_revision",
        "files",
        "post_by_id",
        "fancards",
        "announcements",
        "announcement_count",
        "artist_share_count",
        "discord_channels_for_server",
        "discord_posts",
        "popular_posts",
        "tagged_posts",
        "tags",
        "file",
        "archive_files",
        "linked_accounts",
        "running_imports"
      ]
    },
    "archive-server": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "enabled": {
          "type": "boolean"
        },
        "api_url": {
          "type": "string"
        },
        "serve_files": {
          "type": "boolean"
        },
        "file_serving_enabled": {
          "type": "boolean"
        }
      }
    },
    "filehaus": {
      "description": "Filehaus configuration.",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "requires_account": {
          "description": "If true, an account will be required for uploading.",
          "type": "boolean"
        },
        "required_roles": {
          "description": "Required account roles for uploading. If set to an empty list, no permissions will be required.",
          "type": "array",
          "uniqueItems": true,
          "items": {
            "enum": ["administrator", "moderator", "uploader"]
          }
        },
        "tus": {
          "description": "`tusd` configuration.",
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "manage": {
              "description": "Automatically manage `tusd` on port 1080. If you intend to store uploads on a different server from Kemono3, set a custom URL instead...",
              "type": "boolean"
            },
            "url": {
              "description": "...right here. Note that if you do not allow automatic management and did not set a custom URL, uploads will be blackholed to a demo instance and Filehaus sharing will not function correctly.\nDo note that using `tusd` instances to enable Filehaus functionality requires the `post-create` hook to be pointed at Kemono's `/shares/tus` endpoint.\n`tusd --hooks-enabled-events post-create -hooks-http \"http://127.0.0.1:6942/shares/tus\" -upload-dir=./data`",
              "type": "string"
            }
          }
        }
      }
    },
    "uwsgi-options": {
      "description": "Set additional uWSGI options if you want. Overrides any of the other options.",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "cheaper-algo": {
          "enum": ["busyness"]
        },
        "cheaper": {
          "type": "integer"
        },
        "cheaper-initial": {
          "type": "integer"
        },
        "cheaper-overload": {
          "type": "integer"
        },
        "cheaper-step": {
          "type": "integer"
        },
        "cheaper-busyness-multiplier": {
          "type": "integer"
        },
        "cheaper-busyness-min": {
          "type": "integer"
        },
        "cheaper-busyness-max": {
          "type": "integer"
        },
        "cheaper-busyness-backlog-alert": {
          "type": "integer"
        },
        "cheaper-busyness-backlog-step": {
          "type": "integer"
        },
        "disable-logging": {
          "type": "boolean"
        }
      }
    }
  }
}
