This commit is contained in:
JacobTech 2023-04-02 16:44:26 -04:00
parent 6f8679ceb1
commit 0406903aa7
43 changed files with 2667 additions and 0 deletions

15
.gitignore vendored
View File

@ -8,6 +8,8 @@
.idea/**/usage.statistics.xml .idea/**/usage.statistics.xml
.idea/**/dictionaries .idea/**/dictionaries
.idea/**/shelf .idea/**/shelf
.idea/**/.idea/**
.idea/**/.idea/
# AWS User-specific # AWS User-specific
.idea/**/aws.xml .idea/**/aws.xml
@ -77,3 +79,16 @@ fabric.properties
# Android studio 3.1+ serialized cache file # Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser .idea/caches/build_file_checksums.ser
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/

16
LuskiServer.sln Normal file
View File

@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuskiServer", "LuskiServer\LuskiServer.csproj", "{80FFB3F8-03AC-450C-AA89-0F01A681AFEC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{80FFB3F8-03AC-450C-AA89-0F01A681AFEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{80FFB3F8-03AC-450C-AA89-0F01A681AFEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{80FFB3F8-03AC-450C-AA89-0F01A681AFEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{80FFB3F8-03AC-450C-AA89-0F01A681AFEC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.DataAnnotations;
using Microsoft.AspNetCore.Mvc.Filters;
namespace LuskiServer.Classes.ActionFilters;
[AttributeUsage(AttributeTargets.Method)]
public class TokenFilterAttribute : Attribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
//token check here
Console.WriteLine("Token Check");
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
private string name;
public double verstion;
public TokenFilterAttribute()
{
name = "token";
verstion = 1.0;
}
}

View File

@ -0,0 +1,29 @@
using System.Text.Json.Serialization;
namespace LuskiServer.Classes;
public class AppConfig
{
[JsonInclude]
[JsonPropertyName("address")]
public string Address { get; set; } = "127.0.0.1";
[JsonInclude]
[JsonPropertyName("database")]
public string Database { get; set; } = "Some database name";
[JsonInclude]
[JsonPropertyName("username")]
public string Username { get; set; } = "Some postgresql username";
[JsonInclude]
[JsonPropertyName("password")]
public string Password { get; set; } = "Some postgresql password";
[JsonInclude]
[JsonPropertyName("connection_name")]
public string CustomeName { get; set; } = "Luski Server";
}
[JsonSerializable(typeof(AppConfig))]
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.Never, GenerationMode = JsonSourceGenerationMode.Default)]
internal partial class AppConfigContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,10 @@
using LuskiServer.Enums;
namespace LuskiServer.Classes.Commands;
public class Command
{
public string Name { get; set; } = default!;
public long ServiceID { get; set; } = default!;
public CommandServiceType Type { get; set; }
}

144
LuskiServer/Classes/EXT.cs Normal file
View File

@ -0,0 +1,144 @@
using System.Diagnostics;
using System.Net.Mime;
using System.Text;
using LuskiServer.Classes.TableDef;
using LuskiServer.Enums;
using LuskiServer.Interfaces;
using Microsoft.AspNetCore.Mvc;
namespace LuskiServer.Classes;
public static class EXT
{/*
public static bool CanTokenRequest<TResult>(this ControllerBase Base, out long id, out TResult? result) where TResult : HTTPResponse, new()
{
if (Base.Request.Headers.ContainsKey("token"))
{
return ValidateToke(Base, out id, out result);
}
result = Base.ShowError<TResult>(ErrorCode.MissingToken);
id = 0;
return false;
}
*/
public static string GetNumberString(this ServerPermissions enu)
{
return ((long)(enu)).ToString();
}
public static bool CanTokenRequest(this ControllerBase Base, out long id, out IActionResult? result)
{
if (Base.Request.Headers.ContainsKey("token"))
{
return ValidateToke(Base, out id, out result);
}
result = Base.ShowError(ErrorCode.MissingToken, "You did not provide a token");
id = 0;
return false;
}
public static IActionResult ShowError(this ControllerBase Base, Exception Error, bool LogInDB = true)
{
if (LogInDB)
{
Tables.Logs.Insert(
Logs.ID.CreateParameter(Luski.Snowflake.GenerateSnowflake(WorkerId.Log).ID),
Logs.Type.CreateParameter(LogType.Error),
Logs.Message.CreateParameter(Error.ToString()));
}
return Base.ShowError(ErrorCode.ServerError, Error.Message);
}
public static IActionResult ShowError(this ControllerBase Base, ErrorCode code, string Error)
{
return Base.StatusCode(403, new HTTPResponse()
{
error = code,
error_message = Error
});
}
/*
public static TReturn ShowError<TReturn>(this ControllerBase Base, ErrorCode code, Exception? Error, bool LogInDB = true) where TReturn : HTTPResponse, new()
{
if (Error is null) return new TReturn()
{
error = ErrorCode.ServerError,
error_message = "Something Went wrong in the error handler."
};
try
{
StackTrace st = new(Error, true);
StackFrame? frame = st.GetFrame(0);
string? controler = Base.RouteData.Values["controller"]?.ToString();
string Line = "'Unknown'";
if (frame is not null) Line = frame.GetFileLineNumber().ToString();
string msg = $"In '{controler}Controller.cs' on line '{Line}' through error '{Error.Message}' and ST'{st}' and ERROR:\n'{Error}' FRAM:\n '{frame}'";
//if (LogInDB) Log(msg, LogType.Error);
return Base.ShowError<TReturn>(code, Error.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex);
return new TReturn()
{
error = ErrorCode.ServerError,
error_message = "Something Went wrong in the error handler"
};
}
}
public static TReturn ShowError<TReturn>(this ControllerBase Base, ErrorCode code, string? Error = null) where TReturn : HTTPResponse, new()
{
Base.Response.ContentType = "application/json";
switch (code)
{
case ErrorCode.Forbidden or ErrorCode.InvalidHeader or ErrorCode.InvalidToken or ErrorCode.MissingToken or ErrorCode.MissingToken or ErrorCode.MissingHeader:
Base.Response.StatusCode = StatusCodes.Status403Forbidden;
break;
}
TReturn hTTPResponse = new()
{
error = code,
error_message = Error
};
return hTTPResponse;
}
*/
private static bool CheckToken(string Token, ref long ID)
{
try
{
string id = Encoding.UTF8.GetString(Convert.FromBase64String(Token.Split('.')[0]));
string? tok = Tables.Users.Read(Users.Token, Users.ID.CreateParameter(long.Parse(id)));
if (!string.IsNullOrWhiteSpace(tok) && tok == Token)
{
ID = long.Parse(id);
return true;
}
return false;
}
catch
{
return false;
}
}
private static bool ValidateToke(ControllerBase Base, out long Id, out IActionResult? result)
{
long id = 0;
if (CheckToken(Base.Request.Headers["token"].First()!, ref id))
{
result = null;
Id = id;
return true;
}
Id = id;
result = Base.ShowError(ErrorCode.InvalidToken, (string)null!);
return false;
}
}

View File

@ -0,0 +1,17 @@
using System.Text.Json.Serialization;
using LuskiServer.Enums;
namespace LuskiServer.Classes;
public class HTTPResponse
{
public ErrorCode? error { get; set; } = default!;
public string? error_message { get; set; } = default!;
}
[JsonSerializable(typeof(HTTPResponse))]
internal partial class HTTPResponseContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,6 @@
namespace LuskiServer.Classes;
public class Login
{
public string login_token { get; set; } = default!;
}

View File

@ -0,0 +1,397 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using LuskiServer.Enums;
using ServerDatabase;
namespace LuskiServer.Classes;
public static class Luski
{
public static Database Database = null!;
public static TResult GetSettings<TResult>(string path, JsonTypeInfo<TResult> TypeInfo, bool EndOnError = false) where TResult : new()
{
TResult? @out;
if (!File.Exists(path))
{
@out = new();
try
{
File.WriteAllText(path, JsonSerializer.Serialize(@out, TypeInfo));
}
catch (Exception e)
{
Console.WriteLine("cant write file at '{0}'. make sure the premisions are set", path);
if (EndOnError) Environment.Exit(0);
}
}
try
{
string Fil = "";
try
{
Fil = File.ReadAllText(path);
}
catch (Exception e)
{
Console.WriteLine("cant read file at '{0}'. make sure the premisions are set", path);
if (EndOnError) Environment.Exit(0);
}
@out = JsonSerializer.Deserialize(Fil, TypeInfo);
if (@out is null)
{
@out = new();
}
}
catch
{
@out = new();
}
try
{
File.WriteAllText(path, JsonSerializer.Serialize(@out, TypeInfo));
}
catch (Exception e)
{
Console.WriteLine("cant write file at '{0}'. make sure the premisions are set", path);
if (EndOnError) Environment.Exit(0);
}
return @out;
}
public static AppConfig Config = null!;
public static class Info
{
public static class Routes
{
public static class Default
{
public const string Base = "v{version:apiVersion}/[controller]";
public const string BaseID = $"{Base}/{{id?}}";
public const string Action = $"{Base}/{{action?}}";
public const string ActionID = $"{Action}/{{id?}}";
}
}
public static readonly DateTime Epoch = new(2023, 1, 1, 0, 0, 0, 0);
public static long Timestamp
{
get
{
double ts = Math.Round(DateTime.Now.Subtract(Epoch).TotalMilliseconds, 0);
return long.Parse(ts.ToString().Replace(".", string.Empty));
}
}
}
public class Snowflake
{
public Snowflake(long ID)
{
this.ID = ID;
Increment = (ushort)((ID << 52) >> 52);
Worker_ID = (ushort)((ID << 47) >> 59);
Server_ID = (ushort)((ID << 42) >> 59);
Timestamp = ID >> 22;
}
public static Snowflake GenerateSnowflake(WorkerId workerID)
{
i++;
if (i > 4096) i = 0;
return new Snowflake((((((Info.Timestamp << 5) | (ushort)0) << 5) | (ushort)workerID) << 12) | i);
}
public long ID { get; }
public long Timestamp { get; }
public ushort Worker_ID { get; }
public ushort Server_ID { get; }
public ushort Increment { get; }
private static ushort i = 0;
}
public static class Encryption
{
public class AES
{
public static byte[] Encrypt(byte[] data, string Password)
{
byte[] salt = RandomNumberGenerator.GetBytes(100);
byte[] passwordBytes = Encoding.UTF8.GetBytes(Password);
Rfc2898DeriveBytes key = new(passwordBytes, salt, 50000);
byte[] encrypted;
using Aes aesAlg = Aes.Create();
aesAlg.KeySize = 256;
aesAlg.BlockSize = 128;
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using MemoryStream msEncrypt = new();
msEncrypt.Write(salt, 0, salt.Length);
using CryptoStream csEncrypt = new(msEncrypt, encryptor, CryptoStreamMode.Write);
csEncrypt.Write(data, 0, data.Length);
csEncrypt.Dispose();
encrypted = msEncrypt.ToArray();
return encrypted;
}
public static byte[] Decrypt(byte[] data, string Password)
{
byte[] salt = new byte[100];
using MemoryStream fsCrypt = new(data);
fsCrypt.Read(salt, 0, salt.Length);
byte[] passwordBytes = Encoding.UTF8.GetBytes(Password);
Rfc2898DeriveBytes key = new(passwordBytes, salt, 50000);
byte[] decrypted = new byte[data.Length - salt.Length];
using Aes aesAlg = Aes.Create();
aesAlg.KeySize = 256;
aesAlg.BlockSize = 128;
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
ICryptoTransform encryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using CryptoStream csEncrypt = new(fsCrypt, encryptor, CryptoStreamMode.Read);
MemoryStream fsOut = new();
int read;
byte[] buffer = new byte[data.Length];
while ((read = csEncrypt.Read(buffer, 0, buffer.Length)) > 0)
{
fsOut.Write(buffer, 0, read);
}
csEncrypt.Dispose();
fsCrypt.Dispose();
decrypted = fsOut.ToArray();
fsOut.Dispose();
return decrypted;
}
}
public static byte[] CalculateHash(string text, byte[] salt, int iteration)
{
Rfc2898DeriveBytes? pbkdf2 = new(text, salt, iteration);
return pbkdf2.GetBytes(64);
}
public static byte[] Hash(byte[] data, byte[]? salt = null)
{
using SHA256 sha = SHA256.Create();
if (salt is null) return sha.ComputeHash(data);
else return sha.ComputeHash(Combine(data, salt));
}
internal const int PasswordVersion = 0;
internal static byte[] RemotePasswordEncrypt(string Base64Password, byte[] salt, int PasswordVersion = PasswordVersion)
{
return PasswordVersion switch
{
0 => Hash(Decrypt(Convert.FromBase64String(Base64Password), Keys.PrivateKey), salt),
_ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)),
};
}
public static class Keys
{
private static readonly RSACryptoServiceProvider RSA = new(4096);
private static RSAParameters? Priv = null;
private static string? DBPriv = null;
private static string? Pub = null;
internal static RSAParameters PrivateKey
{
get
{
if (Priv == null)
{
Priv = RSA.ExportParameters(true);
Pub = RSA.ToXmlString(false);
using RSACryptoServiceProvider rsa = new(4096);
rsa.FromXmlString(PrivateKeyString);
DBPriv = rsa.ToXmlString(true);
}
return (RSAParameters)Priv;
}
}
public static string PrivateKeyString
{
get
{
return "<RSAKeyValue><Modulus>2pR655ycDWZuQiPWlcpvloFo2lmcxZJ88nlwNANOri8y+jCrqYvJLLjHW/PHBzDSx/wHEL6GYm/FIPdywgcHzi1a5tuDcdRBrKKqQU/mTW3z2d38H+BJlUCQT/Dai5SXX52p6ZSQBj2Tb4cHZSv3/6vLh1dtJdHYW+RlnadGCKoLJNBF/FQFPBgRkuP69XfAnyM8RTLzzQAcwdmw00pHOdoYV0Ue70MOF3Vv1InnxmB4uOPW6MK45NPH/1u1NkVcp3OZWRAv3xJHfS5CAwtDddnxe4a9f/grvV0lxx4ZCYRLgoe5Lv+0NVIcL+oXdJUI9geyZpfxDpdjdPRc8IA+t+0yyUHTEjyQ3focmFc8fNkmiaxji5sNvG1pWq8Q8MaqgIk1gjRSKJTnWU9N54BMB4pYgRXdVAj/jPS23tgPlcDZNMKvhzIWnzPKEA5CXICPa5DbNYfUbX4DGZlhFv6xkpMMx6jx6WPqIq+XmfaUq6NG6/E/KjbsKzTRk5C4Jfk6doiP5HSdXwP9+yXb1XRus736YoiIABuoCnj8h7OD7+ux31PcCP9PXFayMMxfxZglhw7GhT7MlNb5E9c6ELTqk7exLVxxXG8VL/RpfgyiUzliOjQIM4xyRGBCTXWbr0kRLoW0T9PzGFJzfu2smxSEdmSR9bkC7/i5T3msYa2RiSk=</Modulus><Exponent>AQAB</Exponent><P>5coLKzyBRhzAi8myKb6neZJTxQ2MgQAtXmWCT0xr1xl7wfJ1oQ2q5EQ/FKKD6ejbAyJoJ0deE9dcWUNaxvUmIa9tqHoqxpwr4dBu0IWY+9ZjbS9AmPEzJF0pDqA6NZ5ctxUZ00WzG+swepfjXOPuXqmCX1aV32zJPBN2fo2nrozOdZDqMUB3AeFu4B8zVJKYEXTzVRLnpvY7GdH2xdL/3XvHKwfnrbGv+ScHuNBP1t4oRbNTGLU6eiyK5YbpSL895HrvZYjrg9fkKQDfL0xGQLJOw/agCbwLNlSLhfBSNOfS6B4ehsMurR71Ckpg+kfzy87DssHk6oUrqFD8EcNSqw==</P><Q>84MfGPF7ivPs4hNj7CcVTh8XgOj25kb4j7X+LXzmNTnBtQ/nO9V2j0NUTg/R5gkwSFEyBIJB9lq/wsuebfS22jmxg7mwgVHFFl7JyqcRaiYY1Dl1HKTKgeCsuY7yP5WQCLfYbQ1G7tcgtcQzlPV86W2pgWsVcxcq8gqbk3k4GbPd30kPmfCPxxtxIhyoQxqPxx/FV12PeN9z+G2A70nPDwtkZ2MiiVj/RttTTPrijd5KjV6zS4jDHeYgfO3NZUviyJ8YvhCKB4ttcjM77BV8HdRgBg4XHO+NwDsMukNvAYaPDEfK42UIhne7CZJ/il8h/4OKxRzfFtAnTx/pQ8Jzew==</Q><DP>CnHzrgRzD9/QtMn3SkR7UmBfZG6oO1jptwfAM6CSqlVjNb6ysB5x7SxY/bQhcOl/wxW2TErHMPmyHfCc2Lxd/lv+DRF4jkydBge2cc4Q1Sm6nUTvl8QnAfkmG58W5kcLidrwsJTTfmpjar8qu5c0x6LG5VSHPX+xagSsdzYzMBEAdYGf05tNjY1Uv+VLLQX42ZpKUUypsQIyT4smv3lG3id5NzCFzHRuPlIS3MjDSE4S4JA1L8NVJCaQLbzDL+ZZhuA7r47YvcZ7fY2nl3vNGbXBNNEqFycwD0kqim0RH4yGHrz3wEJxBbeJhe05mUbaAyKj7KU5pZtmD6GWw6vwPw==</DP><DQ>ZUnZGYr4lGe51J+0JHptRj1wjVJZwJcstLpCq7EUIHeRtzqSODUmR5j97CpwaHrR9oKvh2iW/13n/aKsl5f+pu7wg5YtcN0OWau7y+uKNtj54uyzZeK1ySgnMFfhM8mGS9oMz++B3b7mADVIL2GdP4s5wndESMcOOfdnlwQI7cf/Ne0x7Bo/89XaTRIWezMFMxJoB1sHXoOzvVXvF5lf4yYd8VMu/mpiZJq+H3sL2W7pG7yUX4rXfgxG3zAbC1NxVXm31PcUMucv8xyUhDK7mbzI5DvgKU0LbTYiqSd7eOr7fWQvZD6WOTh8OBMTsf64KYwRoMPNl7OlZigj4udzGQ==</DQ><InverseQ>Z5mTYcXZwGh4CZLG5w80GULXzmPm8UQadZJf2PHqSDSNYFdsNGvYP/H1qtn+ZHr6SGJRF2Q0E0MqBIcAZ+iZ7IaaQ+pDszXUoqBKruuLbNk4u2ClYBWjx4ziKfPzF68utOm49EN+Zh7sTOQvUlAO7STE1iuUUGZbCvNybSoH1EP8J+snMPdlIK0M2vdE/yKI7jyqD6NGPTIJYyfnvwkRDr1cv88MgNmOEed8lnTrw69Su21WL2Eh8ePSxStTKQpVMXdefpfTx7B7TrFwDSw7P22RjHc6qFktBXlixwYjuHZBv2OwBA6ii1jQzZjT/IqJFT99pmOmg2BchQwOfD5WCQ==</InverseQ><D>DG0sOZZT4YfPv+SFXWRcs1vtvUV381wwd0vZHv6LJBLx3SW+30zfpd0Rab8HsyHJnJii7DGbYTrvzup9HzPCyVbBwrslvhHDnrrBXs+EjSVcRigSc9t5Q3TKtN4WsUsZef70JcqOOj9DmQUTsg2YXp7X23y9wKHX9jgbYNBIMSjoU5z1mvKfOOXjZeGefFhlCTG5ykirHJ342eUe4jw9pWJ/8heEuonU4heJQTY1M417f4NlzeYiwyZDygGJi/7QgC1DFrxOrxS9H2GYW3pHSQrCvvOPkBgTSy42/hrOG0DmrBKRlW7wbKbw4aEiLpIPKDGS07X9Udzgc3SLA28/DB9AdX6qZSKEmf33LwK47wLEZkiLbU/IO/kb0MpSpl77TrMDrl1J+3Kgcmz7q9eF83W9519/uZgZpOSO/a/97oMKZSfLeyASVP7K1EcqK23K/wjvaP3nvSMGsiQmqCn4jItgfH5N/tcnkNz2DKz3BPcfgAClVH1MFsjDlBqYSHXIqwAFQ8SDBixrPoLoJDkygFoNIrrXqbArVYqXOAgODo2t0foENa/msPJyZ6m6UPi3bMY3gji5qjaSEcvbBmhlSVyHmumgOJ5Rl+L5dS5jAyPgybcVYKab71XQf5sm1UhA8iFWqYgyglmR/b6F9UTFi1QwZA6rgvELFv7DvqjLbmM=</D></RSAKeyValue>";
}
}
public static string PublicKey
{
get
{
if (Pub == null)
{
Priv = RSA.ExportParameters(true);
Pub = RSA.ToXmlString(false);
using RSACryptoServiceProvider rsa = new(4096);
rsa.FromXmlString(PrivateKeyString);
DBPriv = rsa.ToXmlString(true);
}
return Pub;
}
}
}
public static readonly UnicodeEncoding _encoder = new();
public static byte[] Decrypt(byte[] data, bool multithread = false)
{
return Decrypt(data, Keys.PrivateKey, multithread);
}
public static byte[] Decrypt(byte[] data, string Key, bool multithread = false)
{
using RSACryptoServiceProvider rsa = new();
rsa.FromXmlString(Key);
return Decrypt(data, rsa.ExportParameters(true), multithread);
}
public static byte[] Decrypt(byte[] EncryptedText, RSAParameters Key, bool multithread = false)
{
if (EncryptedText is null) throw new ArgumentNullException(nameof(EncryptedText));
using RSACryptoServiceProvider rsa = new();
rsa.ImportParameters(Key);
int size = rsa.KeySize / 8;
double x = EncryptedText.Length / (double)size;
int bbb = int.Parse(x.ToString().Split('.')[0]);
if (x.ToString().Contains('.')) bbb++;
byte[]? datasplitout = Array.Empty<byte>();
if (multithread)
{
byte[][]? decccc = Array.Empty<byte[]>();
Array.Resize(ref decccc, bbb);
int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 25) * 2.0));
if (num == 0) num = 1;
Parallel.For(0, bbb, new ParallelOptions()
{
MaxDegreeOfParallelism = num
}, i =>
{
decccc[i] = rsa.Decrypt(EncryptedText.Skip(i * size).Take(size).ToArray(), false);
});
foreach (byte[] data in decccc)
{
datasplitout = Combine(datasplitout, data);
}
}
else
{
for (int i = 0; i < bbb; i++)
{
datasplitout = Combine(datasplitout, rsa.Decrypt(EncryptedText.Skip(i * size).Take(size).ToArray(), false));
}
}
return datasplitout;
/*
using (RSACryptoServiceProvider? rsa = new())
{
rsa.ImportParameters(Key);
double x = ((double)EncryptedText.Length / (double)512);
int bbb = int.Parse(x.ToString().Split('.')[0]);
if (x.ToString().Contains('.')) bbb++;
byte[][] datasplit = Array.Empty<byte[]>();
byte[] datasplitout = Array.Empty<byte>();
Array.Resize(ref datasplit, bbb);
for (int i = 0; i < bbb; i++)
{
byte[] fff = EncryptedText.Skip(i * 512).Take(512).ToArray();
datasplit[i] = fff;
datasplitout = Combine(datasplitout, rsa.Decrypt(datasplit[i], false));
}
return datasplitout;
}*/
}
public static byte[] Encrypt(string Text, bool multithread = false)
{
using RSACryptoServiceProvider rsa = new();
rsa.FromXmlString(Keys.PublicKey);
return Encrypt(_encoder.GetBytes(Text), rsa.ExportParameters(false), multithread);
}
public static byte[] Encrypt(string Text, string key, bool multithread = false)
{
using RSACryptoServiceProvider rsa = new();
rsa.FromXmlString(key);
return Encrypt(_encoder.GetBytes(Text), rsa.ExportParameters(false), multithread);
}
public static byte[] Encrypt(byte[] data, string key, bool multithread = false)
{
using RSACryptoServiceProvider rsa = new();
rsa.FromXmlString(key);
return Encrypt(data, rsa.ExportParameters(false), multithread);
}
private static byte[] Combine(byte[] first, byte[] second)
{
byte[] bytes = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, bytes, 0, first.Length);
Buffer.BlockCopy(second, 0, bytes, first.Length, second.Length);
return bytes;
}
public static byte[] Encrypt(string Text, RSAParameters Key, bool multithread = false)
{
return Encrypt(_encoder.GetBytes(Text), Key, multithread);
}
public static byte[] Encrypt(byte[] data, RSAParameters Key, bool multithread = false)
{
using RSACryptoServiceProvider rsa = new();
rsa.ImportParameters(Key);
int size = rsa.KeySize / 8;
double x = data.Length / (double)size;
int bbb = int.Parse(x.ToString().Split('.')[0]);
if (x.ToString().Contains('.')) bbb++;
byte[]? datasplitout = Array.Empty<byte>();
if (multithread)
{
byte[][]? decccc = Array.Empty<byte[]>();
Array.Resize(ref decccc, bbb);
int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 25) * 2.0));
if (num == 0) num = 1;
Parallel.For(0, bbb, new ParallelOptions()
{
MaxDegreeOfParallelism = num
}, i =>
{
decccc[i] = rsa.Encrypt(data.Skip(i * size).Take(size).ToArray(), false);
});
foreach (byte[] dataa in decccc)
{
datasplitout = Combine(datasplitout, dataa);
}
}
else
{
for (int i = 0; i < bbb; i++)
{
datasplitout = Combine(datasplitout, rsa.Encrypt(data.Skip(i * size).Take(size).ToArray(), false));
}
}
return datasplitout;
}
}
}

View File

@ -0,0 +1,26 @@
using System.Diagnostics.CodeAnalysis;
namespace LuskiServer.Classes;
[AttributeUsage(AttributeTargets.Field)]
public class PluginWarningAttribute : Attribute
{
public PluginWarningAttribute([DisallowNull]string Message)
{
_mess = Message;
}
private string _mess;
public string Message
{
get
{
return _mess;
}
set
{
_mess = value;
}
}
}

View File

@ -0,0 +1,14 @@
using ServerDatabase;
namespace LuskiServer.Classes.TableDef;
public static class Categories
{
public static TableColumn<long> ID { get; } = new("id", true);
public static TableColumn<string> Name { get; } = new("name");
public static TableColumn<string> Description { get; } = new("description");
public static TableColumn<long[]> InnerCategories { get; } = new("inner_categories") { DefaultValue = Array.Empty<long>() };
public static TableColumn<long[]> Channels { get; } = new("channels") { DefaultValue = Array.Empty<long>() };
public static TableColumn<long[]> RoleOverides { get; } = new("role_overides") { DefaultValue = Array.Empty<long>() };
public static TableColumn<long[]> UserOverides { get; } = new("member_overides") { DefaultValue = Array.Empty<long>() };
}

View File

@ -0,0 +1,15 @@
using LuskiServer.Enums;
using ServerDatabase;
namespace LuskiServer.Classes.TableDef;
public static class Channels
{
public static TableColumn<long> ID { get; } = new("id", true);
public static TableColumn<ChannelType> Type { get; } = new("type");
public static TableColumn<byte[]> Name { get; } = new("name");
public static TableColumn<byte[]> Description { get; } = new("description");
public static TableColumn<string> Key { get; } = new("key");
public static TableColumn<long[]> RoleOverides { get; } = new("role_overides");
public static TableColumn<long[]> UserOverides { get; } = new("member_overides");
}

View File

@ -0,0 +1,12 @@
using ServerDatabase;
namespace LuskiServer.Classes.TableDef;
public static class Files
{
public static TableColumn<long> ID { get; } = new("id", true);
public static TableColumn<long> Size { get; } = new("size");
public static TableColumn<byte[]> Name { get; } = new("name");
public static TableColumn<byte[]> Hash { get; } = new("hash");
public static TableColumn<byte[]> Data { get; } = new("data");
}

View File

@ -0,0 +1,11 @@
using LuskiServer.Enums;
using ServerDatabase;
namespace LuskiServer.Classes.TableDef;
public static class Logs
{
public static TableColumn<long> ID { get; } = new("id", true);
public static TableColumn<LogType> Type { get; } = new("type");
public static TableColumn<string> Message { get; } = new("message");
}

View File

@ -0,0 +1,13 @@
using ServerDatabase;
namespace LuskiServer.Classes.TableDef;
public static class Messages
{
public static TableColumn<long> ID { get; } = new("id", true);
public static TableColumn<long> ChannelID { get; } = new("channel_id");
public static TableColumn<long> AuthorID { get; } = new("author_id");
public static TableColumn<long> TimeStamp { get; } = new("ts");
public static TableColumn<byte[]> Context { get; } = new("context");
public static TableColumn<long[]> Files { get; } = new("files");
}

View File

@ -0,0 +1,13 @@
using LuskiServer.Enums;
using ServerDatabase;
namespace LuskiServer.Classes.TableDef;
public static class Roles
{
public static TableColumn<long> ID { get; } = new("id", true);
public static TableColumn<string> Name { get; } = new("name");
public static TableColumn<string> Color { get; } = new("color");
public static TableColumn<string> Description { get; } = new("description");
public static TableColumn<ServerPermissions[]> ServerPermissions { get; } = new("server_perms");
}

View File

@ -0,0 +1,11 @@
using LuskiServer.Enums;
using ServerDatabase;
namespace LuskiServer.Classes.TableDef;
public static class ServerRoleOverides
{
public static TableColumn<long> ID { get; } = new("id", true);
public static TableColumn<long> RoleID { get; } = new("role_id");
public static TableColumn<string[]> Overides { get; } = new("overides");
}

View File

@ -0,0 +1,12 @@
using ServerDatabase;
namespace LuskiServer.Classes.TableDef;
public static class SessionTokens
{
public static TableColumn<long> ID { get; } = new("id", true);
public static TableColumn<long> AccountID { get; } = new("account_id");
public static TableColumn<byte[]> Token { get; } = new("token");
public static TableColumn<byte[]> AddressFilter { get; } = new("address_filter");
public static TableColumn<DateTime> TimeFilter { get; } = new("date_filter");
}

View File

@ -0,0 +1,12 @@
using LuskiServer.Enums;
using ServerDatabase;
namespace LuskiServer.Classes.TableDef;
public static class UserRoleOverides
{
public static TableColumn<long> ID { get; } = new("id", true);
public static TableColumn<long> UserID { get; } = new("user_id");
public static TableColumn<long> ParentOverideID { get; } = new("parent_overide_id");
public static TableColumn<string[]> Overides { get; } = new("overides");
}

View File

@ -0,0 +1,24 @@
using LuskiServer.Enums;
using ServerDatabase;
namespace LuskiServer.Classes.TableDef;
public class Users
{
public static TableColumn<long> ID { get; } = new("id", true);
public static TableColumn<string> DisplayName { get; } = new("displayname");
public static TableColumn<long> SelectedChannel { get; } = new("selected_channel");
public static TableColumn<Status> Status { get; } = new("status");
public static TableColumn<PictureType> PictureType { get; } = new("picture_type");
public static TableColumn<byte[]> Picture { get; } = new("picture");
public static TableColumn<long[]> Roles { get; } = new("roles");
public static TableColumn<byte[]> Username { get; } = new("username");
public static TableColumn<byte[]> Password { get; } = new("password");
public static TableColumn<byte[]> Salt { get; } = new("salt");
public static TableColumn<string> LoginToken { get; } = new("login_token");
public static TableColumn<string> SessionKey { get; } = new("session_key");
public static TableColumn<string> WSSTCP { get; } = new("wsstcp");
public static TableColumn<string> Token { get; } = new("token");
public static TableColumn<string[]> OfflineData { get; } = new("offline_data");
public static TableColumn<string> OffileKey { get; } = new("offline_key");
}

View File

@ -0,0 +1,17 @@
using ServerDatabase;
namespace LuskiServer.Classes;
public static class Tables
{
public static Table Users { get; } = new Table("users", null!);
public static Table Roles { get; } = new Table("roles", null!);
public static Table Logs { get; } = new Table("logs", null!);
public static Table Files { get; } = new Table("files", null!);
public static Table Categories { get; } = new Table("categories", null!);
public static Table Channels { get; } = new Table("channels", null!);
public static Table Messages { get; } = new Table("messages", null!);
public static Table ServerRoleOverides { get; } = new Table("role_overides", null!);
public static Table UserRoleOverides { get; } = new Table("user_overides", null!);
public static Table SessionTokens { get; } = new Table("session_tokens", null!);
}

View File

@ -0,0 +1,90 @@
using Asp.Versioning;
namespace LuskiServer;
using Asp.Versioning.ApiExplorer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Text;
/// <summary>
/// Configures the Swagger generation options.
/// </summary>
/// <remarks>This allows API versioning to define a Swagger document per API version after the
/// <see cref="IApiVersionDescriptionProvider"/> service has been resolved from the service container.</remarks>
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{
private readonly IApiVersionDescriptionProvider provider;
/// <summary>
/// Initializes a new instance of the <see cref="ConfigureSwaggerOptions"/> class.
/// </summary>
/// <param name="provider">The <see cref="IApiVersionDescriptionProvider">provider</see> used to generate Swagger documents.</param>
public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => this.provider = provider;
/// <inheritdoc />
public void Configure(SwaggerGenOptions options)
{
// add a swagger document for each discovered API version
// note: you might choose to skip or document deprecated API versions differently
foreach (ApiVersionDescription description in provider.ApiVersionDescriptions)
{
options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
}
}
private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
{
StringBuilder text = new StringBuilder("A simple way to view nice information to understand API v" + description.ApiVersion + ".");
OpenApiInfo info = new OpenApiInfo()
{
Title = "Luski Server API",
Version = description.ApiVersion.ToString(),
//Contact = new OpenApiContact() { Name = "Bill Mei", Email = "bill.mei@somewhere.com" },
//License = new OpenApiLicense() { Name = "MIT", Url = new Uri( "https://opensource.org/licenses/MIT" ) }
};
if (description.IsDeprecated)
{
text.Append(" This API version has been deprecated.");
}
if (description.SunsetPolicy is { } policy)
{
if (policy.Date.HasValue)
{
text.Append(" The API will be sunset on ")
.Append(policy.Date.Value.Date.ToShortDateString())
.Append('.');
}
if (policy.HasLinks)
{
text.AppendLine();
for (int i = 0; i < policy.Links.Count; i++)
{
LinkHeaderValue link = policy.Links[i];
if (link.Type == "text/html")
{
text.AppendLine();
if (link.Title.HasValue)
{
text.Append(link.Title.Value).Append(": ");
}
text.Append(link.LinkTarget.OriginalString);
}
}
}
}
info.Description = text.ToString();
return info;
}
}

View File

@ -0,0 +1,172 @@
using System.Net.Mime;
using System.Security.Cryptography;
using System.Text;
using Asp.Versioning;
using LuskiServer.Classes;
using LuskiServer.Classes.TableDef;
using LuskiServer.Enums;
using Microsoft.AspNetCore.Mvc;
namespace LuskiServer.Controllers.v1;
[ApiVersion(1)]
[ApiController]
public class CreateAccountController : ControllerBase
{
/// <summary>
/// Make a post request to this endpoint for registering an account on the server.
/// </summary>
/// <param name="UsernameRaw">Encrypted email for account</param>
/// <param name="PasRaw">Hashed Password for account</param>
/// <param name="DisplayName">Plain text Username</param>
/// <param name="KeyRaw">Plain text RSA key for server to respond with</param>
/// <param name="Body">Raw Picture data for profile</param>
/// <returns></returns>
[HttpPost]
[DisableRequestSizeLimit]
[Consumes(MediaTypeNames.Image.Jpeg, MediaTypeNames.Image.Gif, MediaTypeNames.Image.Tiff, "image/png")]
[Produces(MediaTypeNames.Application.Json)]
[ProducesResponseType(typeof(Login), StatusCodes.Status201Created)]
[ProducesResponseType(typeof(Login), StatusCodes.Status403Forbidden)]
[Route(Luski.Info.Routes.Default.Base)]
public IActionResult Post([FromBody]byte[] Body, [FromHeader(Name = "username")]string? UsernameRaw, [FromHeader(Name = "password")]string? PasRaw, [FromHeader(Name = "displayname")]string? DisplayName, [FromHeader(Name = "key")]string? KeyRaw)
{
try
{
PictureType pfp;
byte[] PasBytes, Username;
byte[] salt = new byte[100];
try
{
using (RandomNumberGenerator provider = RandomNumberGenerator.Create())
{
provider.GetBytes(salt);
}
try
{
PasBytes = Luski.Encryption.RemotePasswordEncrypt(Luski.Encryption._encoder.GetString(Luski.Encryption.Decrypt(Convert.FromBase64String(PasRaw))), salt);
Username = Luski.Encryption.Decrypt(Convert.FromBase64String(UsernameRaw));
try
{
byte[] g = Luski.Encryption.Encrypt("Test data to send to client", KeyRaw);
}
catch
{
return this.ShowError(ErrorCode.InvalidHeader, "The public keys you gave cant be used to encrypt data");
}
}
catch
{
return this.ShowError(ErrorCode.InvalidHeader, "Make sure your login is encrypted with the server provided public key");
}
}
catch (Exception ex)
{
if (ex.Message.Contains("not found")) return this.ShowError(ErrorCode.MissingHeader, "Missing login infermation");
return this.ShowError(ex);
}
if (CheckUsername(Username))
{
int num = new Random().Next(1000, 1000000000);
int num2 = new Random().Next(1000, 1000000000);
Luski.Snowflake id = Luski.Snowflake.GenerateSnowflake(WorkerId.CreateAccount);
byte[] ID = Encoding.UTF8.GetBytes(id.ID.ToString());
byte[] Timestamp = Encoding.UTF8.GetBytes(DateTime.UtcNow.ToString());
byte[] Number = Encoding.UTF8.GetBytes(num.ToString());
byte[] Number2 = Encoding.UTF8.GetBytes(num2.ToString());
string Token = $"{Convert.ToBase64String(ID)}.{Convert.ToBase64String(Timestamp)}.{Convert.ToBase64String(Number)}.{Convert.ToBase64String(Number2)}";
pfp = GetProfilePictureType(Encoding.UTF8.GetString(Body).ToUpper());
Tables.Users.Insert(
Users.ID.CreateParameter(id.ID),
Users.DisplayName.CreateParameter(DisplayName!.Replace("'", "\'")),
Users.SelectedChannel.CreateParameter(0), //TODO set to default
Users.Status.CreateParameter(Status.Offline),
Users.PictureType.CreateParameter(pfp),
Users.Picture.CreateParameter(Body),
Users.Roles.CreateParameter(Array.Empty<long>()),
Users.Username.CreateParameter(Username),
Users.Password.CreateParameter(PasBytes),
Users.Salt.CreateParameter(salt),
Users.LoginToken.CreateParameter(Token),
Users.SessionKey.CreateParameter(KeyRaw),
Users.WSSTCP.CreateParameter(string.Empty),
Users.Token.CreateParameter(string.Empty),
Users.OfflineData.CreateParameter(Array.Empty<string>()),
Users.OffileKey.CreateParameter(string.Empty));
Thread t = new(o => RegToken((string?)o));
t.Start(Token);
return StatusCode(201, new Login()
{
login_token = Token
});
}
return this.ShowError(ErrorCode.Forbidden, "That email is already being used");
}
catch (Exception ex)
{
return this.ShowError(ex);
}
}
private static PictureType GetProfilePictureType(string headerCode)
{
if (headerCode.StartsWith("FFD8FFE0"))
{
return PictureType.jpeg;
}
else if (headerCode.StartsWith("49492A"))
{
return PictureType.tif;
}
else if (headerCode.StartsWith("424D"))
{
return PictureType.bmp;
}
else if (headerCode.StartsWith("GIF"))
{
return PictureType.gif;
}
else if (headerCode.Remove(0, 1).StartsWith("PNG"))
{
return PictureType.png;
}
else
{
return PictureType.png;
}
}
/// <summary>
/// Cheaks to see Username is free
/// </summary>
/// <param name="Username"></param>
/// <returns>
/// <para>true = Good</para>
/// <para>false = Username Taken</para>
/// </returns>
private static bool CheckUsername(byte[] Username)
{
if (Tables.Users.TryRead(Users.Username, out _, Users.Username.CreateParameter(Username))) return false;
return true;
}
private static void RegToken(string? token)
{
try
{
if (token == null) return;
long id = long.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(token.Split('.')[0])));
Tables.Users.Update(Users.ID, id, Users.LoginToken.CreateParameter(token));
Thread.Sleep(30000);
Tables.Users.Update(Users.ID, id, Users.LoginToken.CreateParameter(string.Empty));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}

View File

@ -0,0 +1,67 @@
using System.Net.Mime;
using System.Text;
using Asp.Versioning;
using LuskiServer.Classes;
using LuskiServer.Classes.TableDef;
using LuskiServer.Enums;
using Microsoft.AspNetCore.Mvc;
namespace LuskiServer.Controllers.v1;
[ApiVersion(1)]
[ApiController]
public class KeysController : ControllerBase
{
/// <summary>
/// Return a public RSA key to encrypt information directed for the server.
/// </summary>
/// <returns></returns>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[Produces(MediaTypeNames.Application.Xml)]
[Route(Luski.Info.Routes.Default.Base + "/PublicKey")]
public IActionResult PublicKey()
{
return File(Encoding.UTF8.GetBytes(Luski.Encryption.Keys.PublicKey), "application/xml");
}
/// <summary>
/// Make a post request to this URL to set you offline data key.
/// </summary>
/// <param name="token">Your Luski token for the server</param>
/// <param name="keyy">The key you want to set for when you go offline</param>
/// <returns></returns>
[HttpPost]
[Consumes(MediaTypeNames.Application.Xml)]
[Produces(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status202Accepted)]
[ProducesResponseType(typeof(HTTPResponse), StatusCodes.Status403Forbidden)]
[Route(Luski.Info.Routes.Default.Base + "/SetOfflineKey")]
public IActionResult SetOfflineKey([FromBody]byte[]? keyy, [FromHeader(Name = "token")]string? token)
{
try
{
string key = "";
if (!this.CanTokenRequest(out long ID, out IActionResult? toc) && toc != null) return toc;
string[]? data = Tables.Users.Read(Users.OfflineData, Users.ID.CreateParameter(ID));
if (data is not null) return this.ShowError(ErrorCode.Forbidden, "you cant change your key untill you download your data");
try
{
byte[] g = Luski.Encryption.Encrypt("Test data to send to client", key);
}
catch
{
return this.ShowError(ErrorCode.InvalidPostData, "The key you sent the server appears to be incorect");
}
Tables.Users.Update(Users.ID, ID,
Users.OffileKey.CreateParameter(key));
return StatusCode(202);
}
catch (Exception ex)
{
return this.ShowError(ex);
}
}
}

View File

@ -0,0 +1,163 @@
using System.Net.Mime;
using System.Text;
using Asp.Versioning;
using LuskiServer.Classes;
using LuskiServer.Classes.ActionFilters;
using LuskiServer.Classes.TableDef;
using LuskiServer.Enums;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace LuskiServer.Controllers.v1;
[ApiVersion(1)]
[ApiController]
public class SocketUserProfileController : ControllerBase
{
/// <summary>
/// Returns the Status of a user.
/// </summary>
/// <param name="id">The Id of the user</param>
/// <returns></returns>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(HTTPResponse), StatusCodes.Status403Forbidden, MediaTypeNames.Application.Json)]
[Produces(MediaTypeNames.Text.Plain, MediaTypeNames.Application.Json)]
[Route(Luski.Info.Routes.Default.Base + "/Status/{id:long}")]
public Status Status(long id)
{
Status status = Tables.Users.Read(Users.Status, Users.ID.CreateParameter(id));
return status;
}
/// <summary>
/// Returns the Avatar for the user.
/// </summary>
/// <param name="id">The ID of the requested user</param>
/// <returns></returns>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(HTTPResponse), StatusCodes.Status403Forbidden)]
[Produces(MediaTypeNames.Image.Jpeg, MediaTypeNames.Image.Gif, MediaTypeNames.Image.Tiff, "image/png", MediaTypeNames.Application.Json)]
[Route(Luski.Info.Routes.Default.Base + "/Avatar/{id:long}")]
public IActionResult? Avatar(long id)
{
if (Tables.Users.TryRead(Users.Picture, out byte[]? image, Users.ID.CreateParameter(id)))
{
return Tables.Users.Read(Users.PictureType, Users.ID.CreateParameter(id)) switch
{
PictureType.png => File(image, "image/png"),
PictureType.jpeg => File(image, "image/jpeg"),
PictureType.bmp => File(image, "image/bmp"),
PictureType.gif => File(image, "image/gif"),
PictureType.ico => File(image, "image/vnd.microsoft.icon"),
PictureType.svg => File(image, "image/svg+xml"),
PictureType.tif => File(image, "image/tiff"),
PictureType.webp => File(image, "image/webp"),
//should never happen
_ => File(image, "image/png"),
};
}
else
{
return StatusCode(403, new HTTPResponse()
{
error = ErrorCode.Forbidden,
error_message = "the user you have givven does not exist"
});
//return File(Encoding.UTF8.GetBytes(obj.ToString()), "application/json");
}
}
public class StatusUpdate
{
[BindRequired]
public Status status { get; set; }
}
/// <summary>
/// Make a post to this endpoint to change the status of you account.
/// </summary>
/// <param name="SentData">Infomation required for status update.</param>
/// <param name="token">The Token for your account.</param>
/// <returns></returns>
[HttpPost]
[ProducesResponseType(StatusCodes.Status202Accepted)]
[ProducesResponseType(typeof(HTTPResponse), StatusCodes.Status403Forbidden)]
[Produces(MediaTypeNames.Application.Json)]
[Consumes(typeof(StatusUpdate), MediaTypeNames.Application.Json)]
[Route(Luski.Info.Routes.Default.Base + "/Status")]
[TokenFilter]
public IActionResult Status([FromBody]StatusUpdate SentData)
{
try
{
if (!this.CanTokenRequest(out long ID, out IActionResult? toc) && toc != null) return toc;
//dynamic? SentData = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(content);
Status NewStatus = SentData.status;
Status OldStatus = Tables.Users.Read(Users.Status, Users.ID.CreateParameter(ID));
if (OldStatus != NewStatus && (int)NewStatus < (int)Enums.Status.MAX && (int)NewStatus >= 0)
{
// JObject @out = new()
// {
// { "id", ID },
// { "before", (int)OldStatus },
// { "after", (int)NewStatus },
// { "type", (int)DataType.Status_Update }
// };
Tables.Users.Update(Users.ID, ID, Users.Status.CreateParameter(NewStatus));
if (NewStatus == Enums.Status.Invisible) NewStatus = Enums.Status.Offline;
// WSS.SendData(SendType.All, @out);
}
return StatusCode(202);
}
catch (Exception ex)
{
return this.ShowError(ex);
}
}
/*
[HttpPost]
[Route(Luski.Info.Route.Version1.Action)]
public async Task<IActionResult> Activity()
{
try
{
if (!this.CanTokenRequest(out long token_id, out HTTPResponse? toc) && toc != null) return StatusCode(403, toc);
string content;
using (StreamReader reader = new(Request.Body))
{
content = reader.ReadToEnd();
}
if (string.IsNullOrEmpty(content)) return StatusCode(403, this.ShowError<HTTPResponse>(ErrorCode.MissingPostData));
dynamic? SentData = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(content);
if (SentData == null) return this.ShowError<HTTPResponse>(ErrorCode.MissingPostData);
if (string.IsNullOrEmpty((string)SentData.activity)) return this.ShowError<HTTPResponse>(ErrorCode.MissingPostData);
string NewActivity = (string)SentData.activity;
string OldActivity = Luski.Database.Read<string>("users", "activity", Luski.Database.CreateParameter("id", token_id));
if (OldActivity != NewActivity)
{
JObject @out = new()
{
{ "id", token_id },
{ "before", OldActivity },
{ "after", NewActivity }
};
Luski.Database.Update("users", "id", token_id, Luski.Database.CreateParameter("activity", NewActivity));
WSS.SendData(SendType.All, Program.Output(DataType.Status_Update, @out));
}
Response.Clear();
Response.StatusCode = StatusCodes.Status200OK;
await Response.StartAsync();
return null;
}
catch (Exception ex)
{
return this.ShowError<HTTPResponse>(ErrorCode.ServerError, ex);
}
}*/
}

View File

@ -0,0 +1,6 @@
namespace LuskiServer.Enums;
public enum ChannelType : short
{
}

View File

@ -0,0 +1,9 @@
using Microsoft.VisualBasic.CompilerServices;
namespace LuskiServer.Enums;
public enum CommandServiceType : short
{
Server,
Plugin
}

View File

@ -0,0 +1,14 @@
namespace LuskiServer.Enums;
public enum ErrorCode
{
MissingToken,
InvalidToken,
MissingPostData,
InvalidPostData,
Forbidden,
ServerError,
MissingHeader,
InvalidHeader,
InvalidURL
}

View File

@ -0,0 +1,6 @@
namespace LuskiServer.Enums;
public enum LogType : short
{
Error
}

View File

@ -0,0 +1,13 @@
namespace LuskiServer.Enums;
public enum PictureType : short
{
png,
jpeg,
bmp,
gif,
ico,
svg,
tif,
webp
}

View File

@ -0,0 +1,15 @@
using System.ComponentModel;
using LuskiServer.Classes;
namespace LuskiServer.Enums;
[Flags]
public enum PluginPerms : long
{
CustomeServerRoleSet = 0b_0000_0001,
AddCommands = 0b_0000_0010,
[PluginWarning("This permission is considred dangerous. The plugin will have private keys to some channels.")]
ReadMessages = 0b_0000_0100,
SendMessages = 0b_0000_1000,
FalseUser = 0b_0001_0000
}

View File

@ -0,0 +1,29 @@
namespace LuskiServer.Enums;
public enum ServerPermissions : long
{
ViewChannels,
ManageChannels,
ViewCategories,
ManageCategories,
ManageRoles,
ViewLogs,
ManageServer,
Invite,
Nickname,
ManageNacknames,
Kick,
Ban,
SendMessages,
SendFiles,
ChannelAndServerPings,
PingSomeone,
ManageMessages,
ReadMessageHistory,
UseServerCommands,
JoinVoice,
SpeakInVoice,
MuteMembers,
DeafenMembers,
MoveMembers
}

View File

@ -0,0 +1,11 @@
namespace LuskiServer.Enums;
public enum Status : short
{
Offline,
Online,
Idle,
DoNotDisturb,
Invisible,
MAX
}

View File

@ -0,0 +1,14 @@
namespace LuskiServer.Enums;
public enum WorkerId
{
Log,
CreateAccount,
Login,
SocketUser,
SocketMessage,
SocketBulkMessage,
SocketChannel,
SocketUserProfile,
SocketUserImage
}

View File

@ -0,0 +1,11 @@
using LuskiServer.Enums;
namespace LuskiServer.Interfaces;
public interface ICommand
{
public string Name { get; set; }
public long ServiceID { get; set; }
public CommandServiceType Type { get; set; }
public string ProcessCommand<T>();
}

View File

@ -0,0 +1,16 @@
using LuskiServer.Enums;
namespace LuskiServer.Interfaces;
public interface IPlugin
{
public string Name { get; }
public string Version { get; }
public string Author { get; }
public string Description { get; }
public PluginPerms GetRequiredPerms();
public PluginPerms GetOptionalPerms();
public void Start(PluginPerms perms);
public void Stop();
}

View File

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net7.0\LuskiServer.xml</DocumentationFile>
<NoWarn>1701;1702;IL2121;1591</NoWarn>
<WarningsAsErrors>;NU1605;SYSLIB0011;CS8625;CS8714;SYSLIB0011</WarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DocumentationFile>bin\Release\net7.0\LuskiServer.xml</DocumentationFile>
<WarningsAsErrors>;NU1605;SYSLIB0011;CS8625;CS8714</WarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.3" />
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0" />
<PackageReference Include="ServerDatabase" Version="2.5.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
</ItemGroup>
</Project>

206
LuskiServer/Program.cs Normal file
View File

@ -0,0 +1,206 @@
using System.Reflection;
using Asp.Versioning.ApiExplorer;
using LuskiServer;
using LuskiServer.Classes;
using LuskiServer.Classes.TableDef;
using LuskiServer.Enums;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using ServerDatabase;
using Swashbuckle.AspNetCore.SwaggerGen;
[assembly: Microsoft.AspNetCore.Mvc.ApiController]
Luski.Config = Luski.GetSettings("/etc/luskiserver/app.json", AppConfigContext.Default.AppConfig, true);
Luski.Database = new Database(Luski.Config.Address,
Luski.Config.Database,
Luski.Config.Username,
Luski.Config.Password,
Luski.Config.CustomeName);
try
{
Luski.Database.ExecuteNonQuery($"CREATE DATABASE {Luski.Config.Database} WITH OWNER = {Luski.Config.Username} ENCODING = 'UTF8' CONNECTION LIMIT = -1 IS_TEMPLATE = False;");
Console.WriteLine("Database Created");
}
catch (Exception e)
{
Console.WriteLine("Database Found");
}
foreach (PropertyInfo prop in typeof(Tables).GetProperties())
{
Table table = (Table)prop.GetValue(null)!;
table.DatabaseHandler = Luski.Database;
Type t = Type.GetType($"LuskiServer.Classes.TableDef.{prop.Name}")!;
foreach(PropertyInfo prop2 in t.GetProperties())
{
table.WithColumn((ITableColumn)prop2.GetValue(null)!);
}
Luski.Database.Tables.Add(table);
}
Luski.Database.RegisterTables();
if (!Luski.Database.VersionsTable.TryRead(Luski.Database.VersionsTable.ID, out _,
Luski.Database.VersionsTable.ID.CreateParameter(0)))
{
Luski.Database.VersionsTable.Insert(Luski.Database.VersionsTable.ID.CreateParameter(0));
}
if (!Tables.Roles.TryRead(Roles.ID, out _, Roles.ID.CreateParameter(0)))
{
Tables.Roles.Insert(
Roles.ID.CreateParameter(0),
Roles.Name.CreateParameter("server"),
Roles.Color.CreateParameter("5,5,5,5"),
Roles.Description.CreateParameter("The default role for the server. Everybody will have this role."),
Roles.ServerPermissions.CreateParameter(new[]
{
ServerPermissions.ViewChannels,
ServerPermissions.ViewCategories,
ServerPermissions.Nickname,
ServerPermissions.SendMessages,
ServerPermissions.SendFiles,
ServerPermissions.ChannelAndServerPings,
ServerPermissions.PingSomeone,
ServerPermissions.ReadMessageHistory,
ServerPermissions.UseServerCommands,
ServerPermissions.JoinVoice,
ServerPermissions.SpeakInVoice
})
);
}
if (!Tables.ServerRoleOverides.TryRead(ServerRoleOverides.ID, out _, Categories.ID.CreateParameter(0)))
{
Tables.ServerRoleOverides.Insert(
ServerRoleOverides.ID.CreateParameter(0),
ServerRoleOverides.RoleID.CreateParameter(0),
ServerRoleOverides.Overides.CreateParameter(new string[]
{
$"{ServerPermissions.ViewCategories.GetNumberString()}:1",
$"{ServerPermissions.ViewChannels.GetNumberString()}:1",
})
);
}
if (!Tables.Categories.TryRead(Categories.ID, out _, Categories.ID.CreateParameter(0)))
{
Tables.Categories.Insert(
Categories.ID.CreateParameter(0),
Categories.Name.CreateParameter("server"),
Categories.Description.CreateParameter(
"The default category for the server. Everybody will see this category."),
Categories.RoleOverides.CreateParameter(new long[1] { 0 })
);
}
var builder = WebApplication.CreateBuilder( args );
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
builder.Services.AddApiVersioning(
options =>
{
// reporting api versions will return the headers
// "api-supported-versions" and "api-deprecated-versions"
options.ReportApiVersions = true;
/* Sunset example
options.Policies.Sunset( 1 )
//.Effective( DateTimeOffset.Now.Subtract( new TimeSpan(9999999) ) )
.Effective( DateTimeOffset.Now.AddDays( 2 ) )
.Link( "policy.html" )
.Title( "Versioning Policy" )
.Type( "text/html" );
*/
} )
.AddMvc()
.AddApiExplorer(
options =>
{
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
// note: the specified format code will format the version as "'v'major[.minor][-status]"
options.GroupNameFormat = "'v'VVV";
// note: this option is only necessary when versioning by url segment. the SubstitutionFormat
// can also be used to control the format of the API version in route templates
options.SubstituteApiVersionInUrl = true;
} );
builder.Services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = actionContext =>
{
string content;
using (StreamReader reader = new(actionContext.HttpContext.Request.Body))
{
content = reader.ReadToEndAsync().Result;
}
if (string.IsNullOrEmpty(content)) return new ObjectResult(new HTTPResponse()
{
error = ErrorCode.MissingPostData,
error_message = "The post information was missing"
})
{
StatusCode = 403
};
return new ObjectResult(new HTTPResponse()
{
error = ErrorCode.InvalidPostData,
error_message = "The provided post information was invalid"
})
{
StatusCode = 403
};
};
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
builder.Services.AddSwaggerGen(
options =>
{
// add a custom operation filter which sets default values
options.OperationFilter<SwaggerDefaultValues>();
var fileName = typeof(Program).Assembly.GetName().Name + ".xml";
var filePath = Path.Combine(AppContext.BaseDirectory, fileName);
// integrate xml comments
options.IncludeXmlComments(filePath);
} );
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseSwagger();
app.UseSwaggerUI(
options =>
{
IReadOnlyList<ApiVersionDescription> descriptions = app.DescribeApiVersions();
// build a swagger endpoint for each discovered API version
foreach ( var description in descriptions )
{
string url = $"/swagger/{description.GroupName}/swagger.json";
string name = description.GroupName.ToUpperInvariant();
options.SwaggerEndpoint(url, name);
}
options.InjectStylesheet("/www/css/SwaggerDark.css");
options.DocumentTitle = "Luski Server API";
} );
app.MapGet("/www/css/SwaggerDark.css", async (CancellationToken t) =>
{
var css = await File.ReadAllBytesAsync("www/css/SwaggerDark.css", t);
return Results.File(css, "text/css");
}).ExcludeFromDescription();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

View File

@ -0,0 +1,41 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:63790",
"sslPort": 44369
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5288;http://10.100.0.153:5287",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7173;http://localhost:5287",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,65 @@
namespace LuskiServer;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Text.Json;
/// <summary>
/// Represents the OpenAPI/Swashbuckle operation filter used to document information provided, but not used.
/// </summary>
/// <remarks>This <see cref="IOperationFilter"/> is only required due to bugs in the <see cref="SwaggerGenerator"/>.
/// Once they are fixed and published, this class can be removed.</remarks>
public class SwaggerDefaultValues : IOperationFilter
{
/// <inheritdoc />
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
ApiDescription apiDescription = context.ApiDescription;
operation.Deprecated |= apiDescription.IsDeprecated();
// REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1752#issue-663991077
foreach (ApiResponseType responseType in context.ApiDescription.SupportedResponseTypes)
{
// REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/b7cf75e7905050305b115dd96640ddd6e74c7ac9/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs#L383-L387
string responseKey = responseType.IsDefaultResponse ? "default" : responseType.StatusCode.ToString();
OpenApiResponse response = operation.Responses[responseKey];
foreach (string contentType in response.Content.Keys)
{
if (!responseType.ApiResponseFormats.Any(x => x.MediaType == contentType))
{
response.Content.Remove(contentType);
}
}
}
if (operation.Parameters == null)
{
return;
}
// REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412
// REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413
foreach (OpenApiParameter parameter in operation.Parameters)
{
ApiParameterDescription description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name);
parameter.Description ??= description.ModelMetadata?.Description;
if (parameter.Schema.Default == null &&
description.DefaultValue != null &&
description.DefaultValue is not DBNull &&
description.ModelMetadata is ModelMetadata modelMetadata )
{
// REF: https://github.com/Microsoft/aspnet-api-versioning/issues/429#issuecomment-605402330
string json = JsonSerializer.Serialize(description.DefaultValue, modelMetadata.ModelType);
parameter.Schema.Default = OpenApiAnyFactory.CreateFromJson(json);
}
parameter.Required |= description.IsRequired;
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,829 @@
a { color: #8c8cfa; }
::-webkit-scrollbar-track-piece { background-color: rgba(255, 255, 255, .2) !important; }
::-webkit-scrollbar-track { background-color: rgba(255, 255, 255, .3) !important; }
::-webkit-scrollbar-thumb { background-color: rgba(255, 255, 255, .5) !important; }
embed[type="application/pdf"] { filter: invert(90%); }
html {
background: #1f1f1f !important;
box-sizing: border-box;
filter: contrast(100%) brightness(100%) saturate(100%);
overflow-y: scroll;
}
body {
background: #1f1f1f;
background-color: #1f1f1f;
background-image: none !important;
}
button, input, select, textarea {
background-color: #1f1f1f;
color: #bfbfbf;
}
font, html { color: #bfbfbf; }
.swagger-ui, .swagger-ui section h3 { color: #b5bac9; }
.swagger-ui a { background-color: transparent; }
.swagger-ui mark {
background-color: #664b00;
color: #bfbfbf;
}
.swagger-ui legend { color: inherit; }
.swagger-ui .debug * { outline: #e6da99 solid 1px; }
.swagger-ui .debug-white * { outline: #fff solid 1px; }
.swagger-ui .debug-black * { outline: #bfbfbf solid 1px; }
.swagger-ui .debug-grid { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTRDOTY4N0U2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTRDOTY4N0Q2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3NjY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3NzY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsBS+GMAAAAjSURBVHjaYvz//z8DLsD4gcGXiYEAGBIKGBne//fFpwAgwAB98AaF2pjlUQAAAABJRU5ErkJggg==) 0 0; }
.swagger-ui .debug-grid-16 { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODYyRjhERDU2N0YyMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODYyRjhERDQ2N0YyMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3QTY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3QjY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvCS01IAAABMSURBVHjaYmR4/5+BFPBfAMFm/MBgx8RAGWCn1AAmSg34Q6kBDKMGMDCwICeMIemF/5QawEipAWwUhwEjMDvbAWlWkvVBwu8vQIABAEwBCph8U6c0AAAAAElFTkSuQmCC) 0 0; }
.swagger-ui .debug-grid-8-solid { background: url(data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAAAAD/4QMxaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzExMSA3OS4xNTgzMjUsIDIwMTUvMDkvMTAtMDE6MTA6MjAgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE1IChNYWNpbnRvc2gpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkIxMjI0OTczNjdCMzExRTZCMkJDRTI0MDgxMDAyMTcxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkIxMjI0OTc0NjdCMzExRTZCMkJDRTI0MDgxMDAyMTcxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QjEyMjQ5NzE2N0IzMTFFNkIyQkNFMjQwODEwMDIxNzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QjEyMjQ5NzI2N0IzMTFFNkIyQkNFMjQwODEwMDIxNzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAbGhopHSlBJiZBQi8vL0JHPz4+P0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHAR0pKTQmND8oKD9HPzU/R0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0f/wAARCAAIAAgDASIAAhEBAxEB/8QAWQABAQAAAAAAAAAAAAAAAAAAAAYBAQEAAAAAAAAAAAAAAAAAAAIEEAEBAAMBAAAAAAAAAAAAAAABADECA0ERAAEDBQAAAAAAAAAAAAAAAAARITFBUWESIv/aAAwDAQACEQMRAD8AoOnTV1QTD7JJshP3vSM3P//Z) 0 0 #1c1c21; }
.swagger-ui .debug-grid-16-solid { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzY3MkJEN0U2N0M1MTFFNkIyQkNFMjQwODEwMDIxNzEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NzY3MkJEN0Y2N0M1MTFFNkIyQkNFMjQwODEwMDIxNzEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3QzY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3RDY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pve6J3kAAAAzSURBVHjaYvz//z8D0UDsMwMjSRoYP5Gq4SPNbRjVMEQ1fCRDg+in/6+J1AJUxsgAEGAA31BAJMS0GYEAAAAASUVORK5CYII=) 0 0 #1c1c21; }
.swagger-ui .b--black { border-color: #000; }
.swagger-ui .b--near-black { border-color: #121212; }
.swagger-ui .b--dark-gray { border-color: #333; }
.swagger-ui .b--mid-gray { border-color: #545454; }
.swagger-ui .b--gray { border-color: #787878; }
.swagger-ui .b--silver { border-color: #999; }
.swagger-ui .b--light-silver { border-color: #6e6e6e; }
.swagger-ui .b--moon-gray { border-color: #4d4d4d; }
.swagger-ui .b--light-gray { border-color: #2b2b2b; }
.swagger-ui .b--near-white { border-color: #242424; }
.swagger-ui .b--white { border-color: #1c1c21; }
.swagger-ui .b--white-90 { border-color: rgba(28, 28, 33, .9); }
.swagger-ui .b--white-80 { border-color: rgba(28, 28, 33, .8); }
.swagger-ui .b--white-70 { border-color: rgba(28, 28, 33, .7); }
.swagger-ui .b--white-60 { border-color: rgba(28, 28, 33, .6); }
.swagger-ui .b--white-50 { border-color: rgba(28, 28, 33, .5); }
.swagger-ui .b--white-40 { border-color: rgba(28, 28, 33, .4); }
.swagger-ui .b--white-30 { border-color: rgba(28, 28, 33, .3); }
.swagger-ui .b--white-20 { border-color: rgba(28, 28, 33, .2); }
.swagger-ui .b--white-10 { border-color: rgba(28, 28, 33, .1); }
.swagger-ui .b--white-05 { border-color: rgba(28, 28, 33, .05); }
.swagger-ui .b--white-025 { border-color: rgba(28, 28, 33, .024); }
.swagger-ui .b--white-0125 { border-color: rgba(28, 28, 33, .01); }
.swagger-ui .b--black-90 { border-color: rgba(0, 0, 0, .9); }
.swagger-ui .b--black-80 { border-color: rgba(0, 0, 0, .8); }
.swagger-ui .b--black-70 { border-color: rgba(0, 0, 0, .7); }
.swagger-ui .b--black-60 { border-color: rgba(0, 0, 0, .6); }
.swagger-ui .b--black-50 { border-color: rgba(0, 0, 0, .5); }
.swagger-ui .b--black-40 { border-color: rgba(0, 0, 0, .4); }
.swagger-ui .b--black-30 { border-color: rgba(0, 0, 0, .3); }
.swagger-ui .b--black-20 { border-color: rgba(0, 0, 0, .2); }
.swagger-ui .b--black-10 { border-color: rgba(0, 0, 0, .1); }
.swagger-ui .b--black-05 { border-color: rgba(0, 0, 0, .05); }
.swagger-ui .b--black-025 { border-color: rgba(0, 0, 0, .024); }
.swagger-ui .b--black-0125 { border-color: rgba(0, 0, 0, .01); }
.swagger-ui .b--dark-red { border-color: #bc2f36; }
.swagger-ui .b--red { border-color: #c83932; }
.swagger-ui .b--light-red { border-color: #ab3c2b; }
.swagger-ui .b--orange { border-color: #cc6e33; }
.swagger-ui .b--purple { border-color: #5e2ca5; }
.swagger-ui .b--light-purple { border-color: #672caf; }
.swagger-ui .b--dark-pink { border-color: #ab2b81; }
.swagger-ui .b--hot-pink { border-color: #c03086; }
.swagger-ui .b--pink { border-color: #8f2464; }
.swagger-ui .b--light-pink { border-color: #721d4d; }
.swagger-ui .b--dark-green { border-color: #1c6e50; }
.swagger-ui .b--green { border-color: #279b70; }
.swagger-ui .b--light-green { border-color: #228762; }
.swagger-ui .b--navy { border-color: #0d1d35; }
.swagger-ui .b--dark-blue { border-color: #20497e; }
.swagger-ui .b--blue { border-color: #4380d0; }
.swagger-ui .b--light-blue { border-color: #20517e; }
.swagger-ui .b--lightest-blue { border-color: #143a52; }
.swagger-ui .b--washed-blue { border-color: #0c312d; }
.swagger-ui .b--washed-green { border-color: #0f3d2c; }
.swagger-ui .b--washed-red { border-color: #411010; }
.swagger-ui .b--transparent { border-color: transparent; }
.swagger-ui .b--gold, .swagger-ui .b--light-yellow, .swagger-ui .b--washed-yellow, .swagger-ui .b--yellow { border-color: #664b00; }
.swagger-ui .shadow-1 { box-shadow: rgba(0, 0, 0, .2) 0 0 4px 2px; }
.swagger-ui .shadow-2 { box-shadow: rgba(0, 0, 0, .2) 0 0 8px 2px; }
.swagger-ui .shadow-3 { box-shadow: rgba(0, 0, 0, .2) 2px 2px 4px 2px; }
.swagger-ui .shadow-4 { box-shadow: rgba(0, 0, 0, .2) 2px 2px 8px 0; }
.swagger-ui .shadow-5 { box-shadow: rgba(0, 0, 0, .2) 4px 4px 8px 0; }
@media screen and (min-width: 30em) {
.swagger-ui .shadow-1-ns { box-shadow: rgba(0, 0, 0, .2) 0 0 4px 2px; }
.swagger-ui .shadow-2-ns { box-shadow: rgba(0, 0, 0, .2) 0 0 8px 2px; }
.swagger-ui .shadow-3-ns { box-shadow: rgba(0, 0, 0, .2) 2px 2px 4px 2px; }
.swagger-ui .shadow-4-ns { box-shadow: rgba(0, 0, 0, .2) 2px 2px 8px 0; }
.swagger-ui .shadow-5-ns { box-shadow: rgba(0, 0, 0, .2) 4px 4px 8px 0; }
}
@media screen and (max-width: 60em) and (min-width: 30em) {
.swagger-ui .shadow-1-m { box-shadow: rgba(0, 0, 0, .2) 0 0 4px 2px; }
.swagger-ui .shadow-2-m { box-shadow: rgba(0, 0, 0, .2) 0 0 8px 2px; }
.swagger-ui .shadow-3-m { box-shadow: rgba(0, 0, 0, .2) 2px 2px 4px 2px; }
.swagger-ui .shadow-4-m { box-shadow: rgba(0, 0, 0, .2) 2px 2px 8px 0; }
.swagger-ui .shadow-5-m { box-shadow: rgba(0, 0, 0, .2) 4px 4px 8px 0; }
}
@media screen and (min-width: 60em) {
.swagger-ui .shadow-1-l { box-shadow: rgba(0, 0, 0, .2) 0 0 4px 2px; }
.swagger-ui .shadow-2-l { box-shadow: rgba(0, 0, 0, .2) 0 0 8px 2px; }
.swagger-ui .shadow-3-l { box-shadow: rgba(0, 0, 0, .2) 2px 2px 4px 2px; }
.swagger-ui .shadow-4-l { box-shadow: rgba(0, 0, 0, .2) 2px 2px 8px 0; }
.swagger-ui .shadow-5-l { box-shadow: rgba(0, 0, 0, .2) 4px 4px 8px 0; }
}
.swagger-ui .black-05 { color: rgba(191, 191, 191, .05); }
.swagger-ui .bg-black-05 { background-color: rgba(0, 0, 0, .05); }
.swagger-ui .black-90, .swagger-ui .hover-black-90:focus, .swagger-ui .hover-black-90:hover { color: rgba(191, 191, 191, .9); }
.swagger-ui .black-80, .swagger-ui .hover-black-80:focus, .swagger-ui .hover-black-80:hover { color: rgba(191, 191, 191, .8); }
.swagger-ui .black-70, .swagger-ui .hover-black-70:focus, .swagger-ui .hover-black-70:hover { color: rgba(191, 191, 191, .7); }
.swagger-ui .black-60, .swagger-ui .hover-black-60:focus, .swagger-ui .hover-black-60:hover { color: rgba(191, 191, 191, .6); }
.swagger-ui .black-50, .swagger-ui .hover-black-50:focus, .swagger-ui .hover-black-50:hover { color: rgba(191, 191, 191, .5); }
.swagger-ui .black-40, .swagger-ui .hover-black-40:focus, .swagger-ui .hover-black-40:hover { color: rgba(191, 191, 191, .4); }
.swagger-ui .black-30, .swagger-ui .hover-black-30:focus, .swagger-ui .hover-black-30:hover { color: rgba(191, 191, 191, .3); }
.swagger-ui .black-20, .swagger-ui .hover-black-20:focus, .swagger-ui .hover-black-20:hover { color: rgba(191, 191, 191, .2); }
.swagger-ui .black-10, .swagger-ui .hover-black-10:focus, .swagger-ui .hover-black-10:hover { color: rgba(191, 191, 191, .1); }
.swagger-ui .hover-white-90:focus, .swagger-ui .hover-white-90:hover, .swagger-ui .white-90 { color: rgba(255, 255, 255, .9); }
.swagger-ui .hover-white-80:focus, .swagger-ui .hover-white-80:hover, .swagger-ui .white-80 { color: rgba(255, 255, 255, .8); }
.swagger-ui .hover-white-70:focus, .swagger-ui .hover-white-70:hover, .swagger-ui .white-70 { color: rgba(255, 255, 255, .7); }
.swagger-ui .hover-white-60:focus, .swagger-ui .hover-white-60:hover, .swagger-ui .white-60 { color: rgba(255, 255, 255, .6); }
.swagger-ui .hover-white-50:focus, .swagger-ui .hover-white-50:hover, .swagger-ui .white-50 { color: rgba(255, 255, 255, .5); }
.swagger-ui .hover-white-40:focus, .swagger-ui .hover-white-40:hover, .swagger-ui .white-40 { color: rgba(255, 255, 255, .4); }
.swagger-ui .hover-white-30:focus, .swagger-ui .hover-white-30:hover, .swagger-ui .white-30 { color: rgba(255, 255, 255, .3); }
.swagger-ui .hover-white-20:focus, .swagger-ui .hover-white-20:hover, .swagger-ui .white-20 { color: rgba(255, 255, 255, .2); }
.swagger-ui .hover-white-10:focus, .swagger-ui .hover-white-10:hover, .swagger-ui .white-10 { color: rgba(255, 255, 255, .1); }
.swagger-ui .hover-moon-gray:focus, .swagger-ui .hover-moon-gray:hover, .swagger-ui .moon-gray { color: #ccc; }
.swagger-ui .hover-light-gray:focus, .swagger-ui .hover-light-gray:hover, .swagger-ui .light-gray { color: #ededed; }
.swagger-ui .hover-near-white:focus, .swagger-ui .hover-near-white:hover, .swagger-ui .near-white { color: #f5f5f5; }
.swagger-ui .dark-red, .swagger-ui .hover-dark-red:focus, .swagger-ui .hover-dark-red:hover { color: #e6999d; }
.swagger-ui .hover-red:focus, .swagger-ui .hover-red:hover, .swagger-ui .red { color: #e69d99; }
.swagger-ui .hover-light-red:focus, .swagger-ui .hover-light-red:hover, .swagger-ui .light-red { color: #e6a399; }
.swagger-ui .hover-orange:focus, .swagger-ui .hover-orange:hover, .swagger-ui .orange { color: #e6b699; }
.swagger-ui .gold, .swagger-ui .hover-gold:focus, .swagger-ui .hover-gold:hover { color: #e6d099; }
.swagger-ui .hover-yellow:focus, .swagger-ui .hover-yellow:hover, .swagger-ui .yellow { color: #e6da99; }
.swagger-ui .hover-light-yellow:focus, .swagger-ui .hover-light-yellow:hover, .swagger-ui .light-yellow { color: #ede6b6; }
.swagger-ui .hover-purple:focus, .swagger-ui .hover-purple:hover, .swagger-ui .purple { color: #b99ae4; }
.swagger-ui .hover-light-purple:focus, .swagger-ui .hover-light-purple:hover, .swagger-ui .light-purple { color: #bb99e6; }
.swagger-ui .dark-pink, .swagger-ui .hover-dark-pink:focus, .swagger-ui .hover-dark-pink:hover { color: #e699cc; }
.swagger-ui .hot-pink, .swagger-ui .hover-hot-pink:focus, .swagger-ui .hover-hot-pink:hover, .swagger-ui .hover-pink:focus, .swagger-ui .hover-pink:hover, .swagger-ui .pink { color: #e699c7; }
.swagger-ui .hover-light-pink:focus, .swagger-ui .hover-light-pink:hover, .swagger-ui .light-pink { color: #edb6d5; }
.swagger-ui .dark-green, .swagger-ui .green, .swagger-ui .hover-dark-green:focus, .swagger-ui .hover-dark-green:hover, .swagger-ui .hover-green:focus, .swagger-ui .hover-green:hover { color: #99e6c9; }
.swagger-ui .hover-light-green:focus, .swagger-ui .hover-light-green:hover, .swagger-ui .light-green { color: #a1e8ce; }
.swagger-ui .hover-navy:focus, .swagger-ui .hover-navy:hover, .swagger-ui .navy { color: #99b8e6; }
.swagger-ui .blue, .swagger-ui .dark-blue, .swagger-ui .hover-blue:focus, .swagger-ui .hover-blue:hover, .swagger-ui .hover-dark-blue:focus, .swagger-ui .hover-dark-blue:hover { color: #99bae6; }
.swagger-ui .hover-light-blue:focus, .swagger-ui .hover-light-blue:hover, .swagger-ui .light-blue { color: #a9cbea; }
.swagger-ui .hover-lightest-blue:focus, .swagger-ui .hover-lightest-blue:hover, .swagger-ui .lightest-blue { color: #d6e9f5; }
.swagger-ui .hover-washed-blue:focus, .swagger-ui .hover-washed-blue:hover, .swagger-ui .washed-blue { color: #f7fdfc; }
.swagger-ui .hover-washed-green:focus, .swagger-ui .hover-washed-green:hover, .swagger-ui .washed-green { color: #ebfaf4; }
.swagger-ui .hover-washed-yellow:focus, .swagger-ui .hover-washed-yellow:hover, .swagger-ui .washed-yellow { color: #fbf9ef; }
.swagger-ui .hover-washed-red:focus, .swagger-ui .hover-washed-red:hover, .swagger-ui .washed-red { color: #f9e7e7; }
.swagger-ui .color-inherit, .swagger-ui .hover-inherit:focus, .swagger-ui .hover-inherit:hover { color: inherit; }
.swagger-ui .bg-black-90, .swagger-ui .hover-bg-black-90:focus, .swagger-ui .hover-bg-black-90:hover { background-color: rgba(0, 0, 0, .9); }
.swagger-ui .bg-black-80, .swagger-ui .hover-bg-black-80:focus, .swagger-ui .hover-bg-black-80:hover { background-color: rgba(0, 0, 0, .8); }
.swagger-ui .bg-black-70, .swagger-ui .hover-bg-black-70:focus, .swagger-ui .hover-bg-black-70:hover { background-color: rgba(0, 0, 0, .7); }
.swagger-ui .bg-black-60, .swagger-ui .hover-bg-black-60:focus, .swagger-ui .hover-bg-black-60:hover { background-color: rgba(0, 0, 0, .6); }
.swagger-ui .bg-black-50, .swagger-ui .hover-bg-black-50:focus, .swagger-ui .hover-bg-black-50:hover { background-color: rgba(0, 0, 0, .5); }
.swagger-ui .bg-black-40, .swagger-ui .hover-bg-black-40:focus, .swagger-ui .hover-bg-black-40:hover { background-color: rgba(0, 0, 0, .4); }
.swagger-ui .bg-black-30, .swagger-ui .hover-bg-black-30:focus, .swagger-ui .hover-bg-black-30:hover { background-color: rgba(0, 0, 0, .3); }
.swagger-ui .bg-black-20, .swagger-ui .hover-bg-black-20:focus, .swagger-ui .hover-bg-black-20:hover { background-color: rgba(0, 0, 0, .2); }
.swagger-ui .bg-white-90, .swagger-ui .hover-bg-white-90:focus, .swagger-ui .hover-bg-white-90:hover { background-color: rgba(28, 28, 33, .9); }
.swagger-ui .bg-white-80, .swagger-ui .hover-bg-white-80:focus, .swagger-ui .hover-bg-white-80:hover { background-color: rgba(28, 28, 33, .8); }
.swagger-ui .bg-white-70, .swagger-ui .hover-bg-white-70:focus, .swagger-ui .hover-bg-white-70:hover { background-color: rgba(28, 28, 33, .7); }
.swagger-ui .bg-white-60, .swagger-ui .hover-bg-white-60:focus, .swagger-ui .hover-bg-white-60:hover { background-color: rgba(28, 28, 33, .6); }
.swagger-ui .bg-white-50, .swagger-ui .hover-bg-white-50:focus, .swagger-ui .hover-bg-white-50:hover { background-color: rgba(28, 28, 33, .5); }
.swagger-ui .bg-white-40, .swagger-ui .hover-bg-white-40:focus, .swagger-ui .hover-bg-white-40:hover { background-color: rgba(28, 28, 33, .4); }
.swagger-ui .bg-white-30, .swagger-ui .hover-bg-white-30:focus, .swagger-ui .hover-bg-white-30:hover { background-color: rgba(28, 28, 33, .3); }
.swagger-ui .bg-white-20, .swagger-ui .hover-bg-white-20:focus, .swagger-ui .hover-bg-white-20:hover { background-color: rgba(28, 28, 33, .2); }
.swagger-ui .bg-black, .swagger-ui .hover-bg-black:focus, .swagger-ui .hover-bg-black:hover { background-color: #000; }
.swagger-ui .bg-near-black, .swagger-ui .hover-bg-near-black:focus, .swagger-ui .hover-bg-near-black:hover { background-color: #121212; }
.swagger-ui .bg-dark-gray, .swagger-ui .hover-bg-dark-gray:focus, .swagger-ui .hover-bg-dark-gray:hover { background-color: #333; }
.swagger-ui .bg-mid-gray, .swagger-ui .hover-bg-mid-gray:focus, .swagger-ui .hover-bg-mid-gray:hover { background-color: #545454; }
.swagger-ui .bg-gray, .swagger-ui .hover-bg-gray:focus, .swagger-ui .hover-bg-gray:hover { background-color: #787878; }
.swagger-ui .bg-silver, .swagger-ui .hover-bg-silver:focus, .swagger-ui .hover-bg-silver:hover { background-color: #999; }
.swagger-ui .bg-white, .swagger-ui .hover-bg-white:focus, .swagger-ui .hover-bg-white:hover { background-color: #1c1c21; }
.swagger-ui .bg-transparent, .swagger-ui .hover-bg-transparent:focus, .swagger-ui .hover-bg-transparent:hover { background-color: transparent; }
.swagger-ui .bg-dark-red, .swagger-ui .hover-bg-dark-red:focus, .swagger-ui .hover-bg-dark-red:hover { background-color: #bc2f36; }
.swagger-ui .bg-red, .swagger-ui .hover-bg-red:focus, .swagger-ui .hover-bg-red:hover { background-color: #c83932; }
.swagger-ui .bg-light-red, .swagger-ui .hover-bg-light-red:focus, .swagger-ui .hover-bg-light-red:hover { background-color: #ab3c2b; }
.swagger-ui .bg-orange, .swagger-ui .hover-bg-orange:focus, .swagger-ui .hover-bg-orange:hover { background-color: #cc6e33; }
.swagger-ui .bg-gold, .swagger-ui .bg-light-yellow, .swagger-ui .bg-washed-yellow, .swagger-ui .bg-yellow, .swagger-ui .hover-bg-gold:focus, .swagger-ui .hover-bg-gold:hover, .swagger-ui .hover-bg-light-yellow:focus, .swagger-ui .hover-bg-light-yellow:hover, .swagger-ui .hover-bg-washed-yellow:focus, .swagger-ui .hover-bg-washed-yellow:hover, .swagger-ui .hover-bg-yellow:focus, .swagger-ui .hover-bg-yellow:hover { background-color: #664b00; }
.swagger-ui .bg-purple, .swagger-ui .hover-bg-purple:focus, .swagger-ui .hover-bg-purple:hover { background-color: #5e2ca5; }
.swagger-ui .bg-light-purple, .swagger-ui .hover-bg-light-purple:focus, .swagger-ui .hover-bg-light-purple:hover { background-color: #672caf; }
.swagger-ui .bg-dark-pink, .swagger-ui .hover-bg-dark-pink:focus, .swagger-ui .hover-bg-dark-pink:hover { background-color: #ab2b81; }
.swagger-ui .bg-hot-pink, .swagger-ui .hover-bg-hot-pink:focus, .swagger-ui .hover-bg-hot-pink:hover { background-color: #c03086; }
.swagger-ui .bg-pink, .swagger-ui .hover-bg-pink:focus, .swagger-ui .hover-bg-pink:hover { background-color: #8f2464; }
.swagger-ui .bg-light-pink, .swagger-ui .hover-bg-light-pink:focus, .swagger-ui .hover-bg-light-pink:hover { background-color: #721d4d; }
.swagger-ui .bg-dark-green, .swagger-ui .hover-bg-dark-green:focus, .swagger-ui .hover-bg-dark-green:hover { background-color: #1c6e50; }
.swagger-ui .bg-green, .swagger-ui .hover-bg-green:focus, .swagger-ui .hover-bg-green:hover { background-color: #279b70; }
.swagger-ui .bg-light-green, .swagger-ui .hover-bg-light-green:focus, .swagger-ui .hover-bg-light-green:hover { background-color: #228762; }
.swagger-ui .bg-navy, .swagger-ui .hover-bg-navy:focus, .swagger-ui .hover-bg-navy:hover { background-color: #0d1d35; }
.swagger-ui .bg-dark-blue, .swagger-ui .hover-bg-dark-blue:focus, .swagger-ui .hover-bg-dark-blue:hover { background-color: #20497e; }
.swagger-ui .bg-blue, .swagger-ui .hover-bg-blue:focus, .swagger-ui .hover-bg-blue:hover { background-color: #4380d0; }
.swagger-ui .bg-light-blue, .swagger-ui .hover-bg-light-blue:focus, .swagger-ui .hover-bg-light-blue:hover { background-color: #20517e; }
.swagger-ui .bg-lightest-blue, .swagger-ui .hover-bg-lightest-blue:focus, .swagger-ui .hover-bg-lightest-blue:hover { background-color: #143a52; }
.swagger-ui .bg-washed-blue, .swagger-ui .hover-bg-washed-blue:focus, .swagger-ui .hover-bg-washed-blue:hover { background-color: #0c312d; }
.swagger-ui .bg-washed-green, .swagger-ui .hover-bg-washed-green:focus, .swagger-ui .hover-bg-washed-green:hover { background-color: #0f3d2c; }
.swagger-ui .bg-washed-red, .swagger-ui .hover-bg-washed-red:focus, .swagger-ui .hover-bg-washed-red:hover { background-color: #411010; }
.swagger-ui .bg-inherit, .swagger-ui .hover-bg-inherit:focus, .swagger-ui .hover-bg-inherit:hover { background-color: inherit; }
.swagger-ui .shadow-hover { transition: all .5s cubic-bezier(.165, .84, .44, 1) 0s; }
.swagger-ui .shadow-hover::after {
border-radius: inherit;
box-shadow: rgba(0, 0, 0, .2) 0 0 16px 2px;
content: "";
height: 100%;
left: 0;
opacity: 0;
position: absolute;
top: 0;
transition: opacity .5s cubic-bezier(.165, .84, .44, 1) 0s;
width: 100%;
z-index: -1;
}
.swagger-ui .bg-animate, .swagger-ui .bg-animate:focus, .swagger-ui .bg-animate:hover { transition: background-color .15s ease-in-out 0s; }
.swagger-ui .nested-links a {
color: #99bae6;
transition: color .15s ease-in 0s;
}
.swagger-ui .nested-links a:focus, .swagger-ui .nested-links a:hover {
color: #a9cbea;
transition: color .15s ease-in 0s;
}
.swagger-ui .opblock-tag {
border-bottom: 1px solid rgba(58, 64, 80, .3);
color: #b5bac9;
transition: all .2s ease 0s;
}
.swagger-ui .opblock-tag svg, .swagger-ui section.models h4 svg { transition: all .4s ease 0s; }
.swagger-ui .opblock {
border: 1px solid #000;
border-radius: 4px;
box-shadow: rgba(0, 0, 0, .19) 0 0 3px;
margin: 0 0 15px;
}
.swagger-ui .opblock .tab-header .tab-item.active h4 span::after { background: gray; }
.swagger-ui .opblock.is-open .opblock-summary { border-bottom: 1px solid #000; }
.swagger-ui .opblock .opblock-section-header {
background: rgba(28, 28, 33, .8);
box-shadow: rgba(0, 0, 0, .1) 0 1px 2px;
}
.swagger-ui .opblock .opblock-section-header > label > span { padding: 0 10px 0 0; }
.swagger-ui .opblock .opblock-summary-method {
background: #000;
color: #fff;
text-shadow: rgba(0, 0, 0, .1) 0 1px 0;
}
.swagger-ui .opblock.opblock-post {
background: rgba(72, 203, 144, .1);
border-color: #48cb90;
}
.swagger-ui .opblock.opblock-post .opblock-summary-method, .swagger-ui .opblock.opblock-post .tab-header .tab-item.active h4 span::after { background: #48cb90; }
.swagger-ui .opblock.opblock-post .opblock-summary { border-color: #48cb90; }
.swagger-ui .opblock.opblock-put {
background: rgba(213, 157, 88, .1);
border-color: #d59d58;
}
.swagger-ui .opblock.opblock-put .opblock-summary-method, .swagger-ui .opblock.opblock-put .tab-header .tab-item.active h4 span::after { background: #d59d58; }
.swagger-ui .opblock.opblock-put .opblock-summary { border-color: #d59d58; }
.swagger-ui .opblock.opblock-delete {
background: rgba(200, 50, 50, .1);
border-color: #c83232;
}
.swagger-ui .opblock.opblock-delete .opblock-summary-method, .swagger-ui .opblock.opblock-delete .tab-header .tab-item.active h4 span::after { background: #c83232; }
.swagger-ui .opblock.opblock-delete .opblock-summary { border-color: #c83232; }
.swagger-ui .opblock.opblock-get {
background: rgba(42, 105, 167, .1);
border-color: #2a69a7;
}
.swagger-ui .opblock.opblock-get .opblock-summary-method, .swagger-ui .opblock.opblock-get .tab-header .tab-item.active h4 span::after { background: #2a69a7; }
.swagger-ui .opblock.opblock-get .opblock-summary { border-color: #2a69a7; }
.swagger-ui .opblock.opblock-patch {
background: rgba(92, 214, 188, .1);
border-color: #5cd6bc;
}
.swagger-ui .opblock.opblock-patch .opblock-summary-method, .swagger-ui .opblock.opblock-patch .tab-header .tab-item.active h4 span::after { background: #5cd6bc; }
.swagger-ui .opblock.opblock-patch .opblock-summary { border-color: #5cd6bc; }
.swagger-ui .opblock.opblock-head {
background: rgba(140, 63, 207, .1);
border-color: #8c3fcf;
}
.swagger-ui .opblock.opblock-head .opblock-summary-method, .swagger-ui .opblock.opblock-head .tab-header .tab-item.active h4 span::after { background: #8c3fcf; }
.swagger-ui .opblock.opblock-head .opblock-summary { border-color: #8c3fcf; }
.swagger-ui .opblock.opblock-options {
background: rgba(36, 89, 143, .1);
border-color: #24598f;
}
.swagger-ui .opblock.opblock-options .opblock-summary-method, .swagger-ui .opblock.opblock-options .tab-header .tab-item.active h4 span::after { background: #24598f; }
.swagger-ui .opblock.opblock-options .opblock-summary { border-color: #24598f; }
.swagger-ui .opblock.opblock-deprecated {
background: rgba(46, 46, 46, .1);
border-color: #2e2e2e;
opacity: .6;
}
.swagger-ui .opblock.opblock-deprecated .opblock-summary-method, .swagger-ui .opblock.opblock-deprecated .tab-header .tab-item.active h4 span::after { background: #2e2e2e; }
.swagger-ui .opblock.opblock-deprecated .opblock-summary { border-color: #2e2e2e; }
.swagger-ui .filter .operation-filter-input { border: 2px solid #2b3446; }
.swagger-ui .tab li:first-of-type::after { background: rgba(0, 0, 0, .2); }
.swagger-ui .download-contents {
background: #7c8192;
color: #fff;
}
.swagger-ui .scheme-container {
background: #1c1c21;
box-shadow: rgba(0, 0, 0, .15) 0 1px 2px 0;
}
.swagger-ui .loading-container .loading::before {
animation: 1s linear 0s infinite normal none running rotation, .5s ease 0s 1 normal none running opacity;
border-color: rgba(0, 0, 0, .6) rgba(84, 84, 84, .1) rgba(84, 84, 84, .1);
}
.swagger-ui .response-control-media-type--accept-controller select { border-color: #196619; }
.swagger-ui .response-control-media-type__accept-message { color: #99e699; }
.swagger-ui .version-pragma__message code { background-color: #3b3b3b; }
.swagger-ui .btn {
background: 0 0;
border: 2px solid gray;
box-shadow: rgba(0, 0, 0, .1) 0 1px 2px;
color: #b5bac9;
}
.swagger-ui .btn:hover { box-shadow: rgba(0, 0, 0, .3) 0 0 5px; }
.swagger-ui .btn.authorize, .swagger-ui .btn.cancel {
background-color: transparent;
border-color: #a72a2a;
color: #e69999;
}
.swagger-ui .btn.authorize {
border-color: #48cb90;
color: #9ce3c3;
}
.swagger-ui .btn.authorize svg { fill: #9ce3c3; }
.swagger-ui .btn.execute {
background-color: #5892d5;
border-color: #5892d5;
color: #fff;
}
.swagger-ui .copy-to-clipboard { background: #7c8192; }
.swagger-ui .copy-to-clipboard button { background: url("data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path fill=\"%23fff\" fill-rule=\"evenodd\" d=\"M2 13h4v1H2v-1zm5-6H2v1h5V7zm2 3V8l-3 3 3 3v-2h5v-2H9zM4.5 9H2v1h2.5V9zM2 12h2.5v-1H2v1zm9 1h1v2c-.02.28-.11.52-.3.7-.19.18-.42.28-.7.3H1c-.55 0-1-.45-1-1V4c0-.55.45-1 1-1h3c0-1.11.89-2 2-2 1.11 0 2 .89 2 2h3c.55 0 1 .45 1 1v5h-1V6H1v9h10v-2zM2 5h8c0-.55-.45-1-1-1H8c-.55 0-1-.45-1-1s-.45-1-1-1-1 .45-1 1-.45 1-1 1H3c-.55 0-1 .45-1 1z\"/></svg>") 50% center no-repeat; }
.swagger-ui select {
background: url("data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\"><path d=\"M13.418 7.859a.695.695 0 01.978 0 .68.68 0 010 .969l-3.908 3.83a.697.697 0 01-.979 0l-3.908-3.83a.68.68 0 010-.969.695.695 0 01.978 0L10 11l3.418-3.141z\"/></svg>") right 10px center/20px no-repeat #212121;
background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMS4wICg0MDM1YTRmYjQ5LCAyMDIwLTA1LTAxKSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iZG93bmxvYWQuc3ZnIgogICBpZD0ic3ZnNCIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMjAgMjAiPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTEwIj4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZGVmcwogICAgIGlkPSJkZWZzOCIgLz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ic3ZnNCIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIgogICAgIGlua3NjYXBlOndpbmRvdy15PSItOSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iLTkiCiAgICAgaW5rc2NhcGU6Y3k9IjEwIgogICAgIGlua3NjYXBlOmN4PSIxMCIKICAgICBpbmtzY2FwZTp6b29tPSI0MS41IgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpZD0ibmFtZWR2aWV3NiIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMDAxIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTkyMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIgogICAgIGd1aWRldG9sZXJhbmNlPSIxMCIKICAgICBncmlkdG9sZXJhbmNlPSIxMCIKICAgICBvYmplY3R0b2xlcmFuY2U9IjEwIgogICAgIGJvcmRlcm9wYWNpdHk9IjEiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIiAvPgogIDxwYXRoCiAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZiIKICAgICBpZD0icGF0aDIiCiAgICAgZD0iTTEzLjQxOCA3Ljg1OWEuNjk1LjY5NSAwIDAxLjk3OCAwIC42OC42OCAwIDAxMCAuOTY5bC0zLjkwOCAzLjgzYS42OTcuNjk3IDAgMDEtLjk3OSAwbC0zLjkwOC0zLjgzYS42OC42OCAwIDAxMC0uOTY5LjY5NS42OTUgMCAwMS45NzggMEwxMCAxMWwzLjQxOC0zLjE0MXoiIC8+Cjwvc3ZnPgo=) right 10px center/20px no-repeat #1c1c21;
border: 2px solid #41444e;
}
.swagger-ui select[multiple] { background: #212121; }
.swagger-ui button.invalid, .swagger-ui input[type=email].invalid, .swagger-ui input[type=file].invalid, .swagger-ui input[type=password].invalid, .swagger-ui input[type=search].invalid, .swagger-ui input[type=text].invalid, .swagger-ui select.invalid, .swagger-ui textarea.invalid {
background: #390e0e;
border-color: #c83232;
}
.swagger-ui input[type=email], .swagger-ui input[type=file], .swagger-ui input[type=password], .swagger-ui input[type=search], .swagger-ui input[type=text], .swagger-ui textarea {
background: #1c1c21;
border: 1px solid #404040;
}
.swagger-ui textarea {
background: rgba(28, 28, 33, .8);
color: #b5bac9;
}
.swagger-ui input[disabled], .swagger-ui select[disabled] {
background-color: #1f1f1f;
color: #bfbfbf;
}
.swagger-ui textarea[disabled] {
background-color: #41444e;
color: #fff;
}
.swagger-ui select[disabled] { border-color: #878787; }
.swagger-ui textarea:focus { border: 2px solid #2a69a7; }
.swagger-ui .checkbox input[type=checkbox] + label > .item {
background: #303030;
box-shadow: #303030 0 0 0 2px;
}
.swagger-ui .checkbox input[type=checkbox]:checked + label > .item { background: url("data:image/svg+xml;charset=utf-8,<svg width=\"10\" height=\"8\" viewBox=\"3 7 10 8\" xmlns=\"http://www.w3.org/2000/svg\"><path fill=\"%2341474E\" fill-rule=\"evenodd\" d=\"M6.333 15L3 11.667l1.333-1.334 2 2L11.667 7 13 8.333z\"/></svg>") 50% center no-repeat #303030; }
.swagger-ui .dialog-ux .backdrop-ux { background: rgba(0, 0, 0, .8); }
.swagger-ui .dialog-ux .modal-ux {
background: #1c1c21;
border: 1px solid #2e2e2e;
box-shadow: rgba(0, 0, 0, .2) 0 10px 30px 0;
}
.swagger-ui .dialog-ux .modal-ux-header .close-modal { background: 0 0; }
.swagger-ui .model .deprecated span, .swagger-ui .model .deprecated td { color: #bfbfbf !important; }
.swagger-ui .model-toggle::after { background: url("data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\"><path d=\"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z\"/></svg>") 50% center/100% no-repeat; }
.swagger-ui .model-hint {
background: rgba(0, 0, 0, .7);
color: #ebebeb;
}
.swagger-ui section.models { border: 1px solid rgba(58, 64, 80, .3); }
.swagger-ui section.models.is-open h4 { border-bottom: 1px solid rgba(58, 64, 80, .3); }
.swagger-ui section.models .model-container { background: rgba(0, 0, 0, .05); }
.swagger-ui section.models .model-container:hover { background: rgba(0, 0, 0, .07); }
.swagger-ui .model-box { background: rgba(0, 0, 0, .1); }
.swagger-ui .prop-type { color: #aaaad4; }
.swagger-ui table thead tr td, .swagger-ui table thead tr th {
border-bottom: 1px solid rgba(58, 64, 80, .2);
color: #b5bac9;
}
.swagger-ui .parameter__name.required::after { color: rgba(230, 153, 153, .6); }
.swagger-ui .topbar .download-url-wrapper .select-label { color: #f0f0f0; }
.swagger-ui .topbar .download-url-wrapper .download-url-button {
background: #63a040;
color: #fff;
}
.swagger-ui .info .title small { background: #7c8492; }
.swagger-ui .info .title small.version-stamp { background-color: #7a9b27; }
.swagger-ui .auth-container .errors {
background-color: #350d0d;
color: #b5bac9;
}
.swagger-ui .errors-wrapper {
background: rgba(200, 50, 50, .1);
border: 2px solid #c83232;
}
.swagger-ui .markdown code, .swagger-ui .renderedmarkdown code {
background: rgba(0, 0, 0, .05);
color: #c299e6;
}
.swagger-ui .model-toggle:after { background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMS4wICg0MDM1YTRmYjQ5LCAyMDIwLTA1LTAxKSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iZG93bmxvYWQyLnN2ZyIKICAgaWQ9InN2ZzQiCiAgIHZlcnNpb249IjEuMSIKICAgaGVpZ2h0PSIyNCIKICAgd2lkdGg9IjI0Ij4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGExMCI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGRlZnMKICAgICBpZD0iZGVmczgiIC8+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9InN2ZzQiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTkiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9Ii05IgogICAgIGlua3NjYXBlOmN5PSIxMiIKICAgICBpbmtzY2FwZTpjeD0iMTIiCiAgICAgaW5rc2NhcGU6em9vbT0iMzQuNTgzMzMzIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpZD0ibmFtZWR2aWV3NiIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMDAxIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTkyMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIgogICAgIGd1aWRldG9sZXJhbmNlPSIxMCIKICAgICBncmlkdG9sZXJhbmNlPSIxMCIKICAgICBvYmplY3R0b2xlcmFuY2U9IjEwIgogICAgIGJvcmRlcm9wYWNpdHk9IjEiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIiAvPgogIDxwYXRoCiAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZiIKICAgICBpZD0icGF0aDIiCiAgICAgZD0iTTEwIDZMOC41OSA3LjQxIDEzLjE3IDEybC00LjU4IDQuNTlMMTAgMThsNi02eiIgLz4KPC9zdmc+Cg==) 50% no-repeat; }
.swagger-ui .expand-operation svg, .swagger-ui section.models h4 svg { fill: #fff; }
::-webkit-scrollbar-track { background-color: #646464 !important; }
::-webkit-scrollbar-thumb {
background-color: #242424 !important;
border: 2px solid #3e4346 !important;
}
::-webkit-scrollbar-button:vertical:start:decrement {
background: linear-gradient(130deg, #696969 40%, rgba(255, 0, 0, 0) 41%), linear-gradient(230deg, #696969 40%, transparent 41%), linear-gradient(0deg, #696969 40%, transparent 31%);
background-color: #b6b6b6;
}
::-webkit-scrollbar-button:vertical:end:increment {
background: linear-gradient(310deg, #696969 40%, transparent 41%), linear-gradient(50deg, #696969 40%, transparent 41%), linear-gradient(180deg, #696969 40%, transparent 31%);
background-color: #b6b6b6;
}
::-webkit-scrollbar-button:horizontal:end:increment {
background: linear-gradient(210deg, #696969 40%, transparent 41%), linear-gradient(330deg, #696969 40%, transparent 41%), linear-gradient(90deg, #696969 30%, transparent 31%);
background-color: #b6b6b6;
}
::-webkit-scrollbar-button:horizontal:start:decrement {
background: linear-gradient(30deg, #696969 40%, transparent 41%), linear-gradient(150deg, #696969 40%, transparent 41%), linear-gradient(270deg, #696969 30%, transparent 31%);
background-color: #b6b6b6;
}
::-webkit-scrollbar-button, ::-webkit-scrollbar-track-piece { background-color: #3e4346 !important; }
.swagger-ui .black, .swagger-ui .checkbox, .swagger-ui .dark-gray, .swagger-ui .download-url-wrapper .loading, .swagger-ui .errors-wrapper .errors small, .swagger-ui .fallback, .swagger-ui .filter .loading, .swagger-ui .gray, .swagger-ui .hover-black:focus, .swagger-ui .hover-black:hover, .swagger-ui .hover-dark-gray:focus, .swagger-ui .hover-dark-gray:hover, .swagger-ui .hover-gray:focus, .swagger-ui .hover-gray:hover, .swagger-ui .hover-light-silver:focus, .swagger-ui .hover-light-silver:hover, .swagger-ui .hover-mid-gray:focus, .swagger-ui .hover-mid-gray:hover, .swagger-ui .hover-near-black:focus, .swagger-ui .hover-near-black:hover, .swagger-ui .hover-silver:focus, .swagger-ui .hover-silver:hover, .swagger-ui .light-silver, .swagger-ui .markdown pre, .swagger-ui .mid-gray, .swagger-ui .model .property, .swagger-ui .model .property.primitive, .swagger-ui .model-title, .swagger-ui .near-black, .swagger-ui .parameter__extension, .swagger-ui .parameter__in, .swagger-ui .prop-format, .swagger-ui .renderedmarkdown pre, .swagger-ui .response-col_links .response-undocumented, .swagger-ui .response-col_status .response-undocumented, .swagger-ui .silver, .swagger-ui section.models h4, .swagger-ui section.models h5, .swagger-ui span.token-not-formatted, .swagger-ui span.token-string, .swagger-ui table.headers .header-example, .swagger-ui table.model tr.description, .swagger-ui table.model tr.extension { color: #bfbfbf; }
.swagger-ui .hover-white:focus, .swagger-ui .hover-white:hover, .swagger-ui .info .title small pre, .swagger-ui .topbar a, .swagger-ui .white { color: #fff; }
.swagger-ui .bg-black-10, .swagger-ui .hover-bg-black-10:focus, .swagger-ui .hover-bg-black-10:hover, .swagger-ui .stripe-dark:nth-child(2n + 1) { background-color: rgba(0, 0, 0, .1); }
.swagger-ui .bg-white-10, .swagger-ui .hover-bg-white-10:focus, .swagger-ui .hover-bg-white-10:hover, .swagger-ui .stripe-light:nth-child(2n + 1) { background-color: rgba(28, 28, 33, .1); }
.swagger-ui .bg-light-silver, .swagger-ui .hover-bg-light-silver:focus, .swagger-ui .hover-bg-light-silver:hover, .swagger-ui .striped--light-silver:nth-child(2n + 1) { background-color: #6e6e6e; }
.swagger-ui .bg-moon-gray, .swagger-ui .hover-bg-moon-gray:focus, .swagger-ui .hover-bg-moon-gray:hover, .swagger-ui .striped--moon-gray:nth-child(2n + 1) { background-color: #4d4d4d; }
.swagger-ui .bg-light-gray, .swagger-ui .hover-bg-light-gray:focus, .swagger-ui .hover-bg-light-gray:hover, .swagger-ui .striped--light-gray:nth-child(2n + 1) { background-color: #2b2b2b; }
.swagger-ui .bg-near-white, .swagger-ui .hover-bg-near-white:focus, .swagger-ui .hover-bg-near-white:hover, .swagger-ui .striped--near-white:nth-child(2n + 1) { background-color: #242424; }
.swagger-ui .opblock-tag:hover, .swagger-ui section.models h4:hover { background: rgba(0, 0, 0, .02); }
.swagger-ui .checkbox p, .swagger-ui .dialog-ux .modal-ux-content h4, .swagger-ui .dialog-ux .modal-ux-content p, .swagger-ui .dialog-ux .modal-ux-header h3, .swagger-ui .errors-wrapper .errors h4, .swagger-ui .errors-wrapper hgroup h4, .swagger-ui .info .base-url, .swagger-ui .info .title, .swagger-ui .info h1, .swagger-ui .info h2, .swagger-ui .info h3, .swagger-ui .info h4, .swagger-ui .info h5, .swagger-ui .info li, .swagger-ui .info p, .swagger-ui .info table, .swagger-ui .loading-container .loading::after, .swagger-ui .model, .swagger-ui .opblock .opblock-section-header h4, .swagger-ui .opblock .opblock-section-header > label, .swagger-ui .opblock .opblock-summary-description, .swagger-ui .opblock .opblock-summary-operation-id, .swagger-ui .opblock .opblock-summary-path, .swagger-ui .opblock .opblock-summary-path__deprecated, .swagger-ui .opblock-description-wrapper, .swagger-ui .opblock-description-wrapper h4, .swagger-ui .opblock-description-wrapper p, .swagger-ui .opblock-external-docs-wrapper, .swagger-ui .opblock-external-docs-wrapper h4, .swagger-ui .opblock-external-docs-wrapper p, .swagger-ui .opblock-tag small, .swagger-ui .opblock-title_normal, .swagger-ui .opblock-title_normal h4, .swagger-ui .opblock-title_normal p, .swagger-ui .parameter__name, .swagger-ui .parameter__type, .swagger-ui .response-col_links, .swagger-ui .response-col_status, .swagger-ui .responses-inner h4, .swagger-ui .responses-inner h5, .swagger-ui .scheme-container .schemes > label, .swagger-ui .scopes h2, .swagger-ui .servers > label, .swagger-ui .tab li, .swagger-ui label, .swagger-ui select, .swagger-ui table.headers td { color: #b5bac9; }
.swagger-ui .download-url-wrapper .failed, .swagger-ui .filter .failed, .swagger-ui .model-deprecated-warning, .swagger-ui .parameter__deprecated, .swagger-ui .parameter__name.required span, .swagger-ui table.model tr.property-row .star { color: #e69999; }
.swagger-ui .opblock-body pre.microlight, .swagger-ui textarea.curl {
background: #41444e;
border-radius: 4px;
color: #fff;
}
.swagger-ui .expand-methods svg, .swagger-ui .expand-methods:hover svg { fill: #bfbfbf; }
.swagger-ui .auth-container, .swagger-ui .dialog-ux .modal-ux-header { border-bottom: 1px solid #2e2e2e; }
.swagger-ui .topbar .download-url-wrapper .select-label select, .swagger-ui .topbar .download-url-wrapper input[type=text] { border: 2px solid #63a040; }
.swagger-ui .info a, .swagger-ui .info a:hover, .swagger-ui .scopes h2 a { color: #99bde6; }
/* Dark Scrollbar */
::-webkit-scrollbar {
width: 14px;
height: 14px;
}
::-webkit-scrollbar-button {
background-color: #3e4346 !important;
}
::-webkit-scrollbar-track {
background-color: #646464 !important;
}
::-webkit-scrollbar-track-piece {
background-color: #3e4346 !important;
}
::-webkit-scrollbar-thumb {
height: 50px;
background-color: #242424 !important;
border: 2px solid #3e4346 !important;
}
::-webkit-scrollbar-corner {}
::-webkit-resizer {}
::-webkit-scrollbar-button:vertical:start:decrement {
background:
linear-gradient(130deg, #696969 40%, rgba(255, 0, 0, 0) 41%),
linear-gradient(230deg, #696969 40%, rgba(0, 0, 0, 0) 41%),
linear-gradient(0deg, #696969 40%, rgba(0, 0, 0, 0) 31%);
background-color: #b6b6b6;
}
::-webkit-scrollbar-button:vertical:end:increment {
background:
linear-gradient(310deg, #696969 40%, rgba(0, 0, 0, 0) 41%),
linear-gradient(50deg, #696969 40%, rgba(0, 0, 0, 0) 41%),
linear-gradient(180deg, #696969 40%, rgba(0, 0, 0, 0) 31%);
background-color: #b6b6b6;
}
::-webkit-scrollbar-button:horizontal:end:increment {
background:
linear-gradient(210deg, #696969 40%, rgba(0, 0, 0, 0) 41%),
linear-gradient(330deg, #696969 40%, rgba(0, 0, 0, 0) 41%),
linear-gradient(90deg, #696969 30%, rgba(0, 0, 0, 0) 31%);
background-color: #b6b6b6;
}
::-webkit-scrollbar-button:horizontal:start:decrement {
background:
linear-gradient(30deg, #696969 40%, rgba(0, 0, 0, 0) 41%),
linear-gradient(150deg, #696969 40%, rgba(0, 0, 0, 0) 41%),
linear-gradient(270deg, #696969 30%, rgba(0, 0, 0, 0) 31%);
background-color: #b6b6b6;
}