mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 22:44:29 +01:00
Swagger Generator: Merge multiple documents
It's becoming very hard to edit the swagger file as it grows (especially with multiple PRs altering it). This PR allows the swagger file to be generated from multiple jsons instead which are merged in the controller.
This commit is contained in:
@@ -116,10 +116,14 @@ namespace BTCPayServer.Controllers
|
||||
[Route("swagger/v1/swagger.json")]
|
||||
public async Task<IActionResult> Swagger()
|
||||
{
|
||||
var fi = _fileProvider.GetFileInfo("swagger/v1/swagger.template.json");
|
||||
using var stream = fi.CreateReadStream();
|
||||
JObject json = new JObject();
|
||||
var directoryContents = _fileProvider.GetDirectoryContents("swagger/v1");
|
||||
foreach (IFileInfo fi in directoryContents)
|
||||
{
|
||||
await using var stream = fi.CreateReadStream();
|
||||
using var reader = new StreamReader(fi.CreateReadStream());
|
||||
var json = JObject.Parse(await reader.ReadToEndAsync());
|
||||
json.Merge(JObject.Parse(await reader.ReadToEndAsync()));
|
||||
}
|
||||
var servers = new JArray();
|
||||
servers.Add(new JObject(new JProperty("url", HttpContext.Request.GetAbsoluteRoot())));
|
||||
json["servers"] = servers;
|
||||
|
||||
165
BTCPayServer/wwwroot/swagger/v1/swagger.template.api-keys.json
Normal file
165
BTCPayServer/wwwroot/swagger/v1/swagger.template.api-keys.json
Normal file
@@ -0,0 +1,165 @@
|
||||
{
|
||||
"paths": {
|
||||
"/api/v1/api-keys/{apikey}": {
|
||||
"delete": {
|
||||
"tags": [
|
||||
"API Keys"
|
||||
],
|
||||
"summary": "Revoke an API Key",
|
||||
"description": "Revoke the current API key so that it cannot be used anymore",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "apikey",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The API Key to revoke",
|
||||
"schema": { "type": "string" }
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The key has been deleted"
|
||||
},
|
||||
"404": {
|
||||
"description": "The key is not found for this user"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": [ "unrestricted" ],
|
||||
"Basic": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/api-keys/current": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"API Keys"
|
||||
],
|
||||
"summary": "Get the current API Key information",
|
||||
"description": "View information about the current API key",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The key has been deleted"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"API Keys"
|
||||
],
|
||||
"summary": "Revoke the current API Key",
|
||||
"description": "Revoke the current API key so that it cannot be used anymore",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The key was revoked and is no longer usable",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiKeyData"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/api-keys": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"API Keys"
|
||||
],
|
||||
"summary": "Create a new API Key",
|
||||
"description": "Create a new API Key",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Information about the new api key",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiKeyData"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestBody": {
|
||||
"x-name": "request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "string",
|
||||
"description": "The label of the new API Key",
|
||||
"nullable": true
|
||||
},
|
||||
"permissions": {
|
||||
"type": "array",
|
||||
"description": "The permissions granted to this API Key (See API Key Authentication)",
|
||||
"nullable": true,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": [ "unrestricted" ],
|
||||
"Basic": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ApiKeyData": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"apiKey": {
|
||||
"type": "string",
|
||||
"description": "The API Key to use for API Key Authentication",
|
||||
"nullable": false
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"description": "The label given by the user to this API Key",
|
||||
"nullable": false
|
||||
},
|
||||
"permissions": {
|
||||
"type": "array",
|
||||
"description": "The permissions associated to this API Key",
|
||||
"nullable": false,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "API Keys"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"paths": {
|
||||
"/api-keys/authorize": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Authorization"
|
||||
],
|
||||
"summary": "Authorize User",
|
||||
"description": "Redirect the browser to this endpoint to request the user to generate an api-key with specific permissions",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "permissions",
|
||||
"description": "The permissions to request. (See API Key authentication)",
|
||||
"in": "query",
|
||||
"style": "form",
|
||||
"explode": true,
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"nullable": true,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"x-position": 1
|
||||
},
|
||||
{
|
||||
"name": "applicationName",
|
||||
"description": "The name of your application",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"x-position": 2
|
||||
},
|
||||
{
|
||||
"name": "strict",
|
||||
"description": "If permissions are specified, and strict is set to false, it will allow the user to reject some of permissions the application is requesting.",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"nullable": true
|
||||
},
|
||||
"x-position": 3
|
||||
},
|
||||
{
|
||||
"name": "selectiveStores",
|
||||
"description": "If the application is requesting the CanModifyStoreSettings permission and selectiveStores is set to true, this allows the user to only grant permissions to selected stores under the user's control.",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"nullable": true
|
||||
},
|
||||
"x-position": 4
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A HTML form that a user can use to confirm permissions to grant",
|
||||
"content": {
|
||||
"text/html": {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,338 +11,8 @@
|
||||
},
|
||||
"servers": [
|
||||
],
|
||||
"paths": {
|
||||
"/api-keys/authorize": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Authorization"
|
||||
],
|
||||
"summary": "Authorize User",
|
||||
"description": "Redirect the browser to this endpoint to request the user to generate an api-key with specific permissions",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "permissions",
|
||||
"description": "The permissions to request. (See API Key authentication)",
|
||||
"in": "query",
|
||||
"style": "form",
|
||||
"explode": true,
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"nullable": true,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"x-position": 1
|
||||
},
|
||||
{
|
||||
"name": "applicationName",
|
||||
"description": "The name of your application",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"x-position": 2
|
||||
},
|
||||
{
|
||||
"name": "strict",
|
||||
"description": "If permissions are specified, and strict is set to false, it will allow the user to reject some of permissions the application is requesting.",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"nullable": true
|
||||
},
|
||||
"x-position": 3
|
||||
},
|
||||
{
|
||||
"name": "selectiveStores",
|
||||
"description": "If the application is requesting the CanModifyStoreSettings permission and selectiveStores is set to true, this allows the user to only grant permissions to selected stores under the user's control.",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"nullable": true
|
||||
},
|
||||
"x-position": 4
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A HTML form that a user can use to confirm permissions to grant",
|
||||
"content": {
|
||||
"text/html": {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
}
|
||||
},
|
||||
"/api/v1/users/me": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"summary": "Get current user information",
|
||||
"description": "View information about the current user",
|
||||
"operationId": "Users_GetCurrentUser",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Information about the current user",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApplicationUserData"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": [
|
||||
"btcpay.user.canviewprofile"
|
||||
],
|
||||
"Basic": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/users": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"summary": "Create user",
|
||||
"description": "Create a new user.\n\nThis operation can be called without authentication in any of this cases:\n* There is not any administrator yet on the server,\n* The subscriptions are not disabled in the server's policies.\n\nIf the first administrator is created by this call, subscriptions are automatically disabled.",
|
||||
"requestBody": {
|
||||
"x-name": "request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"description": "The email of the new user",
|
||||
"nullable": false
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"description": "The password of the new user"
|
||||
},
|
||||
"isAdministrator": {
|
||||
"type": "boolean",
|
||||
"description": "Make this user administrator (only if you have the `unrestricted` permission of a server administrator)",
|
||||
"nullable": true,
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true,
|
||||
"x-position": 1
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Information about the new user",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApplicationUserData"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "A list of errors that occurred when creating the user",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ValidationProblemDetails"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "If you need to authenticate for this endpoint (ie. the server settings policies lock subscriptions and that an admin already exists)"
|
||||
},
|
||||
"403": {
|
||||
"description": "If you are authenticated but forbidden to create a new user (ie. you don't have the `unrestricted` permission on a server administrator or if you are not administrator and registrations are disabled in the server's policies)"
|
||||
},
|
||||
"429": {
|
||||
"description": "DDoS protection if you are creating more than 2 accounts every minutes (non-admin only)"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": [
|
||||
"btcpay.server.cancreateuser"
|
||||
],
|
||||
"Basic": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/api-keys/{apikey}": {
|
||||
"delete": {
|
||||
"tags": [
|
||||
"API Keys"
|
||||
],
|
||||
"summary": "Revoke an API Key",
|
||||
"description": "Revoke the current API key so that it cannot be used anymore",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "apikey",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The API Key to revoke",
|
||||
"schema": { "type": "string" }
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The key has been deleted"
|
||||
},
|
||||
"404": {
|
||||
"description": "The key is not found for this user"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": [ "unrestricted" ],
|
||||
"Basic": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/api-keys/current": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"API Keys"
|
||||
],
|
||||
"summary": "Get the current API Key information",
|
||||
"description": "View information about the current API key",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The key has been deleted"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"API Keys"
|
||||
],
|
||||
"summary": "Revoke the current API Key",
|
||||
"description": "Revoke the current API key so that it cannot be used anymore",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The key was revoked and is no longer usable",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiKeyData"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/api-keys": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"API Keys"
|
||||
],
|
||||
"summary": "Create a new API Key",
|
||||
"description": "Create a new API Key",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Information about the new api key",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiKeyData"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestBody": {
|
||||
"x-name": "request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "string",
|
||||
"description": "The label of the new API Key",
|
||||
"nullable": true
|
||||
},
|
||||
"permissions": {
|
||||
"type": "array",
|
||||
"description": "The permissions granted to this API Key (See API Key Authentication)",
|
||||
"nullable": true,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": [ "unrestricted" ],
|
||||
"Basic": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ApplicationUserData": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The id of the new user",
|
||||
"nullable": false
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"description": "The email of the new user",
|
||||
"nullable": false
|
||||
},
|
||||
"emailConfirmed": {
|
||||
"type": "boolean",
|
||||
"description": "True if the email has been confirmed by the user"
|
||||
},
|
||||
"requiresEmailConfirmation": {
|
||||
"type": "boolean",
|
||||
"description": "True if the email requires email confirmation to log in"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ValidationProblemDetails": {
|
||||
"allOf": [
|
||||
{
|
||||
@@ -398,30 +68,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApiKeyData": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"apiKey": {
|
||||
"type": "string",
|
||||
"description": "The API Key to use for API Key Authentication",
|
||||
"nullable": false
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"description": "The label given by the user to this API Key",
|
||||
"nullable": false
|
||||
},
|
||||
"permissions": {
|
||||
"type": "array",
|
||||
"description": "The permissions associated to this API Key",
|
||||
"nullable": false,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes": {
|
||||
"API Key": {
|
||||
@@ -446,12 +92,5 @@
|
||||
"Basic": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"name": "Users"
|
||||
},
|
||||
{
|
||||
"name": "API Keys"
|
||||
}
|
||||
]
|
||||
"tags": []
|
||||
}
|
||||
|
||||
145
BTCPayServer/wwwroot/swagger/v1/swagger.template.users.json
Normal file
145
BTCPayServer/wwwroot/swagger/v1/swagger.template.users.json
Normal file
@@ -0,0 +1,145 @@
|
||||
{
|
||||
"paths": {
|
||||
"/api/v1/users/me": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"summary": "Get current user information",
|
||||
"description": "View information about the current user",
|
||||
"operationId": "Users_GetCurrentUser",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Information about the current user",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApplicationUserData"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": [
|
||||
"btcpay.user.canviewprofile"
|
||||
],
|
||||
"Basic": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/users": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"summary": "Create user",
|
||||
"description": "Create a new user.\n\nThis operation can be called without authentication in any of this cases:\n* There is not any administrator yet on the server,\n* The subscriptions are not disabled in the server's policies.\n\nIf the first administrator is created by this call, subscriptions are automatically disabled.",
|
||||
"requestBody": {
|
||||
"x-name": "request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"description": "The email of the new user",
|
||||
"nullable": false
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"description": "The password of the new user"
|
||||
},
|
||||
"isAdministrator": {
|
||||
"type": "boolean",
|
||||
"description": "Make this user administrator (only if you have the `unrestricted` permission of a server administrator)",
|
||||
"nullable": true,
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true,
|
||||
"x-position": 1
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Information about the new user",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApplicationUserData"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "A list of errors that occurred when creating the user",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ValidationProblemDetails"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "If you need to authenticate for this endpoint (ie. the server settings policies lock subscriptions and that an admin already exists)"
|
||||
},
|
||||
"403": {
|
||||
"description": "If you are authenticated but forbidden to create a new user (ie. you don't have the `unrestricted` permission on a server administrator or if you are not administrator and registrations are disabled in the server's policies)"
|
||||
},
|
||||
"429": {
|
||||
"description": "DDoS protection if you are creating more than 2 accounts every minutes (non-admin only)"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": [
|
||||
"btcpay.server.cancreateuser"
|
||||
],
|
||||
"Basic": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ApplicationUserData": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The id of the new user",
|
||||
"nullable": false
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"description": "The email of the new user",
|
||||
"nullable": false
|
||||
},
|
||||
"emailConfirmed": {
|
||||
"type": "boolean",
|
||||
"description": "True if the email has been confirmed by the user"
|
||||
},
|
||||
"requiresEmailConfirmation": {
|
||||
"type": "boolean",
|
||||
"description": "True if the email requires email confirmation to log in"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "Users"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user