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/**/dictionaries
.idea/**/shelf
.idea/**/.idea/**
.idea/**/.idea/
# AWS User-specific
.idea/**/aws.xml
@ -77,3 +79,16 @@ fabric.properties
# Android studio 3.1+ serialized cache file
.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));