aboutsummaryrefslogtreecommitdiff
path: root/ShiftOS.Server.WebAdmin/Program.cs
diff options
context:
space:
mode:
authorMichael <[email protected]>2017-04-02 19:14:11 -0400
committerMichael <[email protected]>2017-04-02 19:14:11 -0400
commit29a88a5c46dff19358b44defdc52bb75db12b9ad (patch)
tree5581ddd8152264de140a84265022da7b69722643 /ShiftOS.Server.WebAdmin/Program.cs
parent313722475456a6d9c0f187a43e5f7afd87160303 (diff)
downloadshiftos_thereturn-29a88a5c46dff19358b44defdc52bb75db12b9ad.tar.gz
shiftos_thereturn-29a88a5c46dff19358b44defdc52bb75db12b9ad.tar.bz2
shiftos_thereturn-29a88a5c46dff19358b44defdc52bb75db12b9ad.zip
Delete MUD webadmin.
Diffstat (limited to 'ShiftOS.Server.WebAdmin/Program.cs')
-rw-r--r--ShiftOS.Server.WebAdmin/Program.cs808
1 files changed, 0 insertions, 808 deletions
diff --git a/ShiftOS.Server.WebAdmin/Program.cs b/ShiftOS.Server.WebAdmin/Program.cs
deleted file mode 100644
index 1e4a0b8..0000000
--- a/ShiftOS.Server.WebAdmin/Program.cs
+++ /dev/null
@@ -1,808 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Nancy;
-using Nancy.Authentication.Forms;
-using Nancy.Bootstrapper;
-using Nancy.Hosting.Self;
-using Nancy.ModelBinding;
-using Nancy.Security;
-using Nancy.TinyIoc;
-using Newtonsoft.Json;
-using ShiftOS.Objects;
-
-namespace ShiftOS.Server.WebAdmin
-{
- class Program
- {
- static void Main(string[] args)
- {
- var HostConf = new HostConfiguration();
- HostConf.UrlReservations.CreateAutomatically = true;
- HostConf.RewriteLocalhost = true;
- using(var nancy = new NancyHost(HostConf, new Uri("http://localhost:13371/mudadmin/")))
- {
- nancy.Start();
- Console.WriteLine($"[{DateTime.Now}] <AdminPanel/NancyInit> Initiating on localhost:13371...");
- Console.ReadLine();
- }
- }
- }
-
- public static class PageBuilder
- {
- public static string Build(string page, Dictionary<string, string> templateParams = null)
- {
- string templatehtml = Properties.Resources.HtmlTemplate;
- if (templateParams == null)
- {
- templateParams = new Dictionary<string, string>();
- }
- if (!templateParams.ContainsKey("{logout}"))
- {
- templateParams.Add("{logout}", "<li><a href=\"/mudadmin/logout\">Log out</a></li>");
- }
- if (SystemManager.MudIsRunning())
- {
- templateParams.Add("{mud_power}", "<li><a href='/mudadmin/poweroff'><span class='glyphicon glyphicon-power-off'></span> Power off</a></li>");
- templateParams.Add("{mud_restart}", "<li><a href='/mudadmin/restart'><span class='glyphicon glyphicon-refresh'></span> Restart</a></li>");
- }
- else
- {
- templateParams.Add("{mud_power}", "<li><a href='/mudadmin/poweron'><span class='glyphicon glyphicon-power-on'></span> Power on</a></li>");
- templateParams.Add("{mud_restart}", "");
- }
-
- if(templateParams["{logout}"] == "")
- {
- templateParams["{mud_power}"] = "";
- templateParams["{mud_restart}"] = "";
-
- }
-
- switch (page)
- {
- case "status":
- templatehtml = templatehtml.Replace("{body}", Properties.Resources.Status);
- break;
- case "login":
- templatehtml = templatehtml.Replace("{body}", Properties.Resources.LoginView);
- break;
- case "initialsetup":
- templatehtml = templatehtml.Replace("{body}", Properties.Resources.SetupView);
- break;
- }
- try
- {
- foreach (var param in templateParams)
- {
- templatehtml = templatehtml.Replace(param.Key, param.Value);
- }
- }
- catch { }
- return templatehtml;
- }
- }
-
- public class MudUserIdentity : IUserIdentity
- {
- public MudUserIdentity(string username)
- {
- _username = username;
- }
-
- public IEnumerable<string> Claims
- {
- get
- {
- return SystemManager.GetClaims(_username);
- }
- }
-
- private string _username = "";
-
- public string UserName
- {
- get
- {
- return _username;
- }
- }
- }
-
- public static class SystemManager
- {
- public static bool MudIsRunning()
- {
- var processes = System.Diagnostics.Process.GetProcessesByName("ShiftOS.Server");
- return processes.Length > 0;
- }
-
- public static void KillMud()
- {
- var processes = System.Diagnostics.Process.GetProcessesByName("ShiftOS.Server");
- for(int i = 0; i < processes.Length; i++)
- {
- try
- {
- processes[i].Kill();
- }
- catch
- {
- }
- }
- }
-
- public static List<string> GetClaims(string username)
- {
- foreach(var save in GetSaves())
- {
- if (save.IsMUDAdmin)
- {
- return new List<string> { "User", "Admin" };
- }
- }
- return new List<string>(new[] { "User" });
- }
-
- public static Save[] GetSaves()
- {
- List<Save> saves = new List<Save>();
- if (Directory.Exists("saves"))
- {
- foreach(var saveFile in Directory.GetFiles("saves"))
- {
- try
- {
- saves.Add(JsonConvert.DeserializeObject<Save>(Server.Program.ReadEncFile(saveFile)));
- }
- catch { }
- }
- }
- return saves.ToArray();
- }
-
- public static bool Login(string username, string password, out Guid id)
- {
- foreach (var user in GetSaves())
- {
- if (user.Username == username && user.Password == password)
- {
- id = user.ID;
- return true;
- }
- }
- id = new Guid();
- return false;
- }
-
- public static string BuildFormFromObject(object obj)
- {
- StringBuilder sb = new StringBuilder();
- sb.AppendLine("<form method='post' action=''><table class='table'>");
- foreach(var prop in obj.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
- {
- string name = "";
- string description = "No description.";
- foreach(var attrib in prop.GetCustomAttributes(false))
- {
- if(attrib is FriendlyNameAttribute)
- {
- name = (attrib as FriendlyNameAttribute).Name;
- }
- if(attrib is FriendlyDescriptionAttribute)
- {
- description = (attrib as FriendlyDescriptionAttribute).Description;
- }
- }
- if (name != "")
- {
- sb.AppendLine("<tr>");
-
- sb.AppendLine($@"<td width=""45%"">
- <p><strong>{name}</strong></p>
- <p>{description}</p>
-</td>
-<td>");
- if (prop.PropertyType == typeof(bool))
- {
- string isChecked = ((bool)prop.GetValue(obj) == true) ? "checked" : "";
- sb.AppendLine($"<input class='form-control' type='checkbox' name='{prop.Name}' {isChecked}/>");
- }
- else if (prop.PropertyType == typeof(string))
- {
- sb.AppendLine($"<input class='form-control' type='text' name='{prop.Name}' value='{prop.GetValue(obj)}'/>");
- }
-
- sb.AppendLine("</td></tr>");
- }
- else
- {
- sb.AppendLine($"<input type='hidden' name='{prop.Name}' value='{prop.GetValue(obj)}'/>");
- }
- }
- sb.AppendLine("<tr><td></td><td><input class='btn btn-default' type='submit'/></td></tr>");
- sb.AppendLine("</table></form>");
- return sb.ToString();
- }
-
- public static Channel GetChat(string id)
- {
- if (File.Exists("chats.json"))
- foreach (var channel in JsonConvert.DeserializeObject<List<Channel>>(File.ReadAllText("chats.json")))
- {
- if (channel.ID == id)
- return channel;
- }
- return new Channel();
- }
-
- public static string BuildSaveListing(Save[] list)
- {
- StringBuilder sb = new StringBuilder();
- sb.AppendLine("<table class=\"table\">");
-
- sb.AppendLine(@"<tr>
- <td><strong>Username</strong></td>
- <td><strong>System Name</strong></td>
- <td><strong>Codepoints</strong></td>
- <td><strong>Shiftorium Upgrades</strong></td>
- <td><strong>Is MUD Admin</strong></td>
- <td><strong>Actions</strong></td>
-</tr>");
-
- foreach(var save in list)
- {
- sb.AppendLine($@"<tr>
- <td>{save.Username}</td>
- <td>{save.SystemName}</td>
- <td>{save.Codepoints}</td>
- <td>{save.CountUpgrades()} installed, {save.Upgrades.Count} total</td>
- <td>{save.IsMUDAdmin}</td>
- <td>
- <a href=""/mudadmin/toggleadmin/{save.Username}"" class=""btn btn-danger"">Toggle admin</a>
- <a href=""/mudadmin/deletesave/{save.Username}"" class=""btn btn-danger"">Delete save</a>
- </td>
-</tr>");
- }
-
- sb.AppendLine("</table>");
- return sb.ToString();
- }
-
-
- public static string GetAllChats()
- {
- StringBuilder sb = new StringBuilder();
- sb.AppendLine("<table class=\"table\">");
- sb.AppendLine($@"<tr><td><strong>ID</strong></td>
- <td><strong>Name</strong></td>
- <td><strong>Topic</strong></td>
- <td><strong>Is Discord Relay</strong></td>
- <td><strong>Discord channel ID</strong></td>
- <td><strong>Discord Bot Token</strong></td>
- <td><strong>Actions</strong></td></tr>");
- if (File.Exists("chats.json"))
- {
- foreach(var chat in JsonConvert.DeserializeObject<List<Channel>>(File.ReadAllText("chats.json")))
- {
- sb.AppendLine($@"<tr>
- <td>{chat.ID}</td>
- <td>{chat.Name}</td>
- <td>{chat.Topic}</td>
- <td>{chat.IsDiscordProxy}</td>
- <td>{chat.DiscordChannelID}</td>
- <td>{chat.DiscordBotToken}</td>
- <td>
- <a href=""/mudadmin/editchat/{chat.ID}"" class=""btn btn-default""><span class=""glyphicon glyphicon-pencil""></span>Edit</a>
- <a href=""#"" class=""btn btn-default"" data-toggle=""modal"" data-target=""#modal_{chat.ID}""><span class=""glyphicon glyphicon-delete""></span> Delete</a>
- </td>
-</tr>");
- sb.AppendLine(CreateModal(chat.ID, "Delete " + chat.Name + "?", "Are you sure you want to delete this chat?", "/deletechat/" + chat.ID));
- }
- }
- sb.AppendLine("</table>");
- return sb.ToString();
- }
-
- public static string CreateModal(string id, string title, string msg, string callbackUrl)
- {
- return $@"<div id=""modal_{id}"" class=""modal fade"" role=""dialog"">
- <div class=""modal-dialog"">
-
- <!-- Modal content-->
- <div class=""modal-content"">
- <div class=""modal-header"">
- <button type=""button"" class=""close"" data-dismiss=""modal"">&times;</button>
- <h4 class=""modal-title"">{title}</h4>
- </div>
- <div class=""modal-body"">
- <p>{msg}</p>
- </div>
- <div class=""modal-footer"">
- <a href=""/mudadmin{callbackUrl}"" class=""btn btn-danger"">Yes</a>
- <button type=""button"" class=""btn btn-default"" data-dismiss=""modal"">No</button>
- </div>
- </div>
-
- </div>
-</div>";
- }
-
- public static string GetCPWorth()
- {
- if (System.IO.Directory.Exists("saves"))
- {
- long cp = 0;
-
- foreach(var file in System.IO.Directory.GetFiles("saves"))
- {
- if (file.EndsWith(".save"))
- {
- var save = JsonConvert.DeserializeObject<Save>(Server.Program.ReadEncFile(file));
- cp += save.Codepoints;
- }
- }
- return cp.ToString();
- }
- else
- {
- return "0";
- }
- }
-
- public static string GetUserCount()
- {
- if (System.IO.Directory.Exists("saves"))
- {
- return System.IO.Directory.GetFiles("saves").Length.ToString();
- }
- else
- {
- return "0";
- }
- }
-
- public static MudUserIdentity GetIdentity(Guid id)
- {
- foreach (var user in GetSaves())
- {
- if (user.ID == id)
- {
- return new WebAdmin.MudUserIdentity(user.Username);
- }
- }
- return null;
- }
-
- internal static void MakeAdmin(string username)
- {
- Save sav = null;
- foreach(var save in GetSaves())
- {
- if (save.Username == username)
- sav = save;
- }
- if(sav != null)
- {
- sav.IsMUDAdmin = true;
- Server.Program.WriteEncFile("saves/" + username + ".save", JsonConvert.SerializeObject(sav));
- }
- }
-
- internal static Save[] GetAdmins()
- {
- var saves = new List<Save>();
- foreach(var save in GetSaves())
- {
- if(save.IsMUDAdmin == true)
- {
- saves.Add(save);
- }
- }
- return saves.ToArray();
- }
- }
-
- public class MudUser
- {
- [FriendlyName("Username")]
- [FriendlyDescription("The username you will appear as in-game.")]
- public string Username { get; set; }
-
- [FriendlyName("Password")]
- [FriendlyDescription("A password that you will use to log in to the admin panel and the game.")]
- public string Password { get; set; }
-
- [FriendlyName("System name")]
- [FriendlyDescription("An in-game hostname for your account. In ShiftOS, your user ID is always yourusername@yoursystemname. Be creative.")]
- public string SystemName { get; set; }
-
- public Guid ID { get; set; }
- }
-
- public class MudBootstrapper : DefaultNancyBootstrapper
- {
- protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
- {
- var formsAuthConfiguration = new FormsAuthenticationConfiguration();
- formsAuthConfiguration.RedirectUrl = "~/login";
- formsAuthConfiguration.UserMapper = container.Resolve<IUserMapper>();
- FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
- base.ApplicationStartup(container, pipelines);
- }
- }
-
-
- public class MudUserMapper : IUserMapper
- {
- public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
- {
- return SystemManager.GetIdentity(identifier);
- }
- }
-
- public class LoginModule : NancyModule
- {
- public LoginModule()
- {
- Get["/login"] = parameters =>
- {
- if (SystemManager.GetSaves().Length > 0)
- {
- if (SystemManager.GetAdmins().Length > 0)
- {
- return PageBuilder.Build("login", new Dictionary<string, string>
- {
- {"{logout}", "" }
- });
- }
- else
- {
- return PageBuilder.Build("initialsetup", new Dictionary<string, string>
- {
- {"{logout}", "" },
- {"{savelist}", BuildSaveList() }
- });
- }
- }
- else
- {
- return PageBuilder.Build("bla", new Dictionary<string, string>
- {
- {"{body}", Properties.Resources.NoUsersFound },
- {"{user_create_form}", SystemManager.BuildFormFromObject(new MudUser()) }
- });
- }
- };
-
- Get["/logout"] = parameters =>
- {
- return this.Logout("~/");
- };
-
- Post["/login"] = parameters =>
- {
- if (SystemManager.GetSaves().Length > 0)
- {
- if (SystemManager.GetAdmins().Length == 0)
- {
- var user = this.Bind<LoginRequest>();
- SystemManager.MakeAdmin(user.username);
- Guid id = new Guid();
- if(SystemManager.Login(user.username, user.password, out id) == true)
- {
- return this.Login(id);
- }
- return new UserModule().Redirect("/login");
- }
- else
- {
- var user = this.Bind<LoginRequest>();
- Guid id = new Guid();
- if (SystemManager.Login(user.username, user.password, out id) == true)
- {
- return this.Login(id);
- }
- return new UserModule().Redirect("/login");
- }
- }
- else
- {
- var newUser = this.Bind<MudUser>();
- var save = new Save();
- save.Username = newUser.Username;
- save.SystemName = newUser.SystemName;
- save.Password = newUser.Password;
- save.Codepoints = 0;
- save.MyShop = "";
- save.Upgrades = new Dictionary<string, bool>();
- save.IsMUDAdmin = true;
- save.StoryPosition = 1;
-
- if (!Directory.Exists("saves"))
- Directory.CreateDirectory("saves");
- save.ID = Guid.NewGuid();
-
- Server.Program.WriteEncFile("saves/" + save.Username + ".save", JsonConvert.SerializeObject(save));
- return this.Login(save.ID);
- }
- };
- }
-
- private string BuildSaveList()
- {
- StringBuilder sb = new StringBuilder();
- sb.AppendLine("<table class='table'>");
- sb.AppendLine($@"<tr>
- <td><strong>Username</strong></td>
- <td><strong>System name</strong></td>
- <td><strong>Codepoints</strong></td>
- <td><strong>Actions</strong></td>
-</tr>");
-
- foreach(var save in SystemManager.GetSaves())
- {
- sb.AppendLine($@"<tr>
- <td>{save.Username}</td>
- <td>{save.SystemName}</td>
- <td>{save.Codepoints}</td>
- <td><form method='post' action=''>
- <input type='hidden' name='username' value='{save.Username}'/><input type='hidden' name='password' value='{save.Password}'/>
- <input type='submit' value='Choose' class='btn btn-default'/>
- </form></td>
-</tr>");
- }
-
- sb.AppendLine("</table>");
- return sb.ToString();
- }
- }
-
-
-
- public class UserModule : NancyModule
- {
- public string Redirect(string url)
- {
- return $@"<html>
- <head>
- <meta http-equiv=""refresh"" content=""0; url=/mudadmin{url}"" />
- </ head>
-</html>";
- }
-
- public UserModule()
- {
- this.RequiresAuthentication();
- this.RequiresClaims("Admin");
- Get["/"] = _ =>
- {
- return Redirect("/status");
- };
-
- Get["/toggleadmin/{id}"] = parameters =>
- {
- string id = parameters.id;
- for (int i = 0; i < SystemManager.GetSaves().Length; i++)
- {
- var save = SystemManager.GetSaves()[i];
- if(save.Username.ToString() == id)
- {
- save.IsMUDAdmin = !save.IsMUDAdmin;
- Server.Program.WriteEncFile("saves/" + save.Username + ".save", JsonConvert.SerializeObject(save));
- }
- }
- return Redirect("/saves");
-
- };
-
- Get["/deletesave/{username}"] = parameters =>
- {
-
-
- string id = parameters.username;
- for (int i = 0; i < SystemManager.GetSaves().Length; i++)
- {
- if (SystemManager.GetSaves()[i].Username.ToString() == id)
- {
- File.Delete("saves/" + SystemManager.GetSaves()[i].Username + ".save");
- }
- }
- return Redirect("/saves");
- };
-
-
- Get["/saves"] = _ =>
- {
- return PageBuilder.Build("bla", new Dictionary<string, string>
- {
- { "{body}", Properties.Resources.GenericTableList },
- { "{listtitle}", "Test subjects" },
- { "{listdesc}", "Below is a list of test subjects (save files) on your multi-user domain. You can see their username, system name, Codepoints, amount of installed upgrades, and you can also perform basic actions on each save." },
- { "{list}", SystemManager.BuildSaveListing(SystemManager.GetSaves()) }
- });
- };
-
- Get["/status"] = _ =>
- {
- return statusBuilder();
- };
-
- Get["/deletechat/{id}"] = parameters =>
- {
- string chatID = parameters.id;
- var chats = JsonConvert.DeserializeObject<List<Channel>>(File.ReadAllText("chats.json"));
- for(int i = 0; i < chats.Count; i++)
- {
- try
- {
- if (chats[i].ID == chatID)
- chats.RemoveAt(i);
- }
- catch { }
- }
- File.WriteAllText("chats.json", JsonConvert.SerializeObject(chats, Formatting.Indented));
- return Redirect("/chats");
- };
-
- Get["/chats"] = _ =>
- {
- return chatsListBuilder();
- };
-
- Get["/createchat"] = _ =>
- {
- return PageBuilder.Build("editchat", new Dictionary<string, string>
- {
- {"{body}", Properties.Resources.ChatEditTemplate },
- {"{form}", SystemManager.BuildFormFromObject(new Channel()) }
- });
- };
-
- Post["/createchat"] = parameters =>
- {
- var chat = this.Bind<Channel>();
- chat.ID = chat.Name.ToLower().Replace(" ", "_");
- List<Channel> chats = new List<Channel>();
- if (File.Exists("chats.json"))
- chats = JsonConvert.DeserializeObject<List<Channel>>(File.ReadAllText("chats.json"));
-
- bool chatExists = false;
-
- for (int i = 0; i < chats.Count; i++)
- {
- if (chats[i].ID == chat.ID)
- {
- chats[i] = chat;
- chatExists = true;
- }
- }
-
- if (!chatExists)
- {
- chats.Add(chat);
- }
-
- File.WriteAllText("chats.json", JsonConvert.SerializeObject(chats, Formatting.Indented));
-
- return Redirect("/chats");
- };
-
- Get["/editchat/{id}"] = parameters =>
- {
- return PageBuilder.Build("editchat", new Dictionary<string, string>
- {
- {"{body}", Properties.Resources.ChatEditTemplate },
- {"{form}", SystemManager.BuildFormFromObject(SystemManager.GetChat(parameters.id)) }
- });
- };
-
- Post["/editchat/{id}"] = parameters =>
- {
- var chat = this.Bind<Channel>();
- chat.ID = chat.Name.ToLower().Replace(" ", "_");
- List<Channel> chats = new List<Channel>();
- if (File.Exists("chats.json"))
- chats = JsonConvert.DeserializeObject<List<Channel>>(File.ReadAllText("chats.json"));
-
- bool chatExists = false;
-
- for (int i = 0; i < chats.Count; i++)
- {
- if (chats[i].ID == chat.ID)
- {
- chats[i] = chat;
- chatExists = true;
- }
- }
-
- if (!chatExists)
- {
- chats.Add(chat);
- }
-
- File.WriteAllText("chats.json", JsonConvert.SerializeObject(chats, Formatting.Indented));
- return Redirect("/chats");
- };
-
- Get["/poweron"] = _ =>
- {
- if (!SystemManager.MudIsRunning())
- {
- System.Diagnostics.Process.Start("ShiftOS.Server.exe");
- }
- return Redirect("/");
- };
-
- Get["/poweroff"] = _ =>
- {
- if (SystemManager.MudIsRunning())
- {
- SystemManager.KillMud();
- }
- return Redirect("/");
- };
- Get["/restart"] = _ =>
- {
- if (SystemManager.MudIsRunning())
- {
- SystemManager.KillMud();
- }
- return Redirect("/poweron");
- };
- }
-
- private string statusBuilder()
- {
- return PageBuilder.Build("status", new Dictionary<string, string>{
- { "{cp_worth}", SystemManager.GetCPWorth() },
- { "{user_count}", SystemManager.GetUserCount() },
- { "{system_time}", DateTime.Now.ToString() },
- });
-
- }
-
- private string chatsListBuilder()
- {
- return PageBuilder.Build("bla", new Dictionary<string, string>
- {
- { "{body}", Properties.Resources.ChatListView },
- { "{chat_table}", SystemManager.GetAllChats() }
- });
- }
- }
-
- public class LoginRequest
- {
- public string username { get; set; }
- public string password { get; set; }
- }
-}