Start of the web UI project

This commit is contained in:
Madpeter 2020-05-11 22:15:34 +01:00
parent 27a152369b
commit 35a54a4e94
20 changed files with 359 additions and 105 deletions

View file

@ -220,7 +220,7 @@ namespace BetterSecondBotShared.Json
{
ConsoleLog.Warn("Http disabled: Httpkey length must be 12 or more");
}
else if (jsonConfig.Httpport < 81)
else if (jsonConfig.Httpport < 80)
{
ConsoleLog.Warn("Http disabled: Httpport range below protected range - Given: (" + jsonConfig.Httpport + ")");
}

View file

@ -1,8 +1,11 @@
using BetterSecondBotShared.Json;
using BetterSecondBotShared.logs;
using Microsoft.Extensions.ObjectPool;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
@ -22,6 +25,54 @@ namespace BetterSecondBotShared.Static
}
public static class helpers
{
//
public static byte[] ReadResourceFileBinary(Assembly targetenv, string filename)
{
try
{
byte[] dataset;
string[] files = targetenv.GetManifestResourceNames();
string resourceName = files.Single(str => str.EndsWith(filename));
using (Stream stream = targetenv.GetManifestResourceStream(resourceName))
using (BinaryReader br = new BinaryReader(stream))
{
const int bufferSize = 4096;
using (var ms = new MemoryStream())
{
byte[] buffer = new byte[bufferSize];
int count;
while ((count = br.Read(buffer, 0, buffer.Length)) != 0)
ms.Write(buffer, 0, count);
dataset = ms.ToArray();
}
}
return dataset;
}
catch (Exception e)
{
return Encoding.UTF8.GetBytes(e.Message);
}
}
public static string ReadResourceFile(Assembly targetenv,string filename)
{
try
{
string[] files = targetenv.GetManifestResourceNames();
string resourceName = files.Single(str => str.EndsWith(filename));
string result = "";
using (Stream stream = targetenv.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
result = reader.ReadToEnd();
}
return result;
}
catch (Exception e)
{
return e.Message;
}
}
public static bool inrange(float current,float min,float max)
{
if(current <= max)

View file

@ -3,7 +3,7 @@
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<AssemblyMetadataCommand Remove="GitDescription"/>
<AssemblyMetadataCommand Remove="GitDescription" />
</ItemGroup>
<PropertyGroup>
<MSBuildGitHashCommand>git describe --long --always</MSBuildGitHashCommand>
@ -26,6 +26,15 @@
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
</PropertyGroup>
<ItemGroup>
<None Remove="HttpWebUi\theme\images\favicon-32x32.png" />
<None Remove="HttpWebUi\theme\images\logo.png" />
<None Remove="HttpWebUi\theme\layout\cdn.css.layout" />
<None Remove="HttpWebUi\theme\layout\cdn.js.layout" />
<None Remove="HttpWebUi\theme\layout\full.layout" />
<None Remove="HttpWebUi\theme\layout\login.block" />
<None Remove="HttpWebUi\theme\layout\sidemenu.layout" />
<None Remove="HttpWebUi\theme\site.css" />
<None Remove="HttpWebUi\theme\site.js" />
<None Remove="WikiMake\wiki_footer.txt" />
<None Remove="WikiMake\wiki_header.txt" />
</ItemGroup>
@ -37,6 +46,15 @@
</PackageReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="HttpWebUi\theme\images\favicon-32x32.png" />
<EmbeddedResource Include="HttpWebUi\theme\images\logo.png" />
<EmbeddedResource Include="HttpWebUi\theme\layout\cdn.css.layout" />
<EmbeddedResource Include="HttpWebUi\theme\layout\cdn.js.layout" />
<EmbeddedResource Include="HttpWebUi\theme\layout\full.layout" />
<EmbeddedResource Include="HttpWebUi\theme\layout\login.block" />
<EmbeddedResource Include="HttpWebUi\theme\layout\sidemenu.layout" />
<EmbeddedResource Include="HttpWebUi\theme\css\site.css" />
<EmbeddedResource Include="HttpWebUi\theme\js\site.js" />
<EmbeddedResource Include="WikiMake\wiki_footer.txt" />
<EmbeddedResource Include="WikiMake\wiki_header.txt" />
</ItemGroup>

View file

@ -1,4 +1,5 @@
using BetterSecondBotShared.IO;
using BetterSecondBot.HttpWebUi;
using BetterSecondBotShared.IO;
using BetterSecondBotShared.Json;
using BetterSecondBotShared.logs;
using BetterSecondBotShared.Static;
@ -22,8 +23,7 @@ namespace BetterSecondBot.HttpServer
public SecondBot GetBot { get { return Bot; } }
protected JsonConfig Config;
public JsonConfig GetConfig { get { return Config; } }
protected HTTPCommandsInterfaceGet get_controler;
protected HTTPCommandsInterfacePost post_controler;
protected HTTPCommandsInterface post_controler;
protected string url = "";
public bool ShutdownHTTP { get; set; }
public bool HTTPCnCmode { get; set; }
@ -93,6 +93,7 @@ namespace BetterSecondBot.HttpServer
protected async Task HandleIncomingConnections()
{
webui myUI = new webui(Config,Bot);
while (Bot.KillMe == false)
{
HttpListenerContext ctx = await listener.GetContextAsync();
@ -107,16 +108,11 @@ namespace BetterSecondBot.HttpServer
byte[] data;
resp.StatusCode = 200;
resp.ContentType = "text/html";
resp.ContentEncoding = Encoding.UTF8;
if (req.HttpMethod == "GET")
{
string command = "status";
if (http_args.Count > 0)
{
command = http_args.Last();
}
string content = String.Join("{@}", get_controler.Call(command, ""));
data = Encoding.UTF8.GetBytes(content);
KeyValuePair<string, byte[]> replydata = myUI.process(http_args, req.Cookies);
resp.ContentType = replydata.Key;
data = replydata.Value;
resp.ContentLength64 = data.LongLength;
await resp.OutputStream.WriteAsync(data, 0, data.Length);
resp.Close();
@ -190,8 +186,7 @@ namespace BetterSecondBot.HttpServer
}
protected void OpenHttpService()
{
get_controler = new HTTPCommandsInterfaceGet(Bot,this);
post_controler = new HTTPCommandsInterfacePost(Bot,this);
post_controler = new HTTPCommandsInterface(Bot,this);
Thread t = new Thread(delegate (object _)
{
bool ok = true;

View file

@ -4,7 +4,7 @@ using System.Text;
namespace BetterSecondBot.HttpServer.Control.Actions
{
public class Logout : HTTP_commands_post
public class Logout : HTTP_commands
{
public override string Helpfile { get { return "Makes the bot logout<br/> Returns OK,I dont want to go if accepted"; } }

View file

@ -5,7 +5,7 @@ using System.Text;
namespace BetterSecondBot.HttpServer.Control.Interface
{
public class CoreCMD : HTTP_commands_post
public class CoreCMD : HTTP_commands
{
public override string[] ArgTypes { get { return new [] { "Text","Text" }; } }
public override string[] ArgHints { get { return new [] { "Command","Args (~#~ split)" }; } }

View file

@ -11,15 +11,17 @@ using BetterSecondBotShared.logs;
namespace BetterSecondBot.HttpServer
{
public abstract class HTTPCommandsInterface : API_supported_interface
public class HTTPCommandsInterface : API_supported_interface
{
protected SecondBot bot;
protected SecondBotHttpServer httpserver;
protected HTTPCommandsInterface(SecondBot linktobot, SecondBotHttpServer sethttpserver)
public HTTPCommandsInterface(SecondBot linktobot, SecondBotHttpServer sethttpserver)
{
bot = linktobot;
httpserver = sethttpserver;
API_type = typeof(HTTP_commands);
LoadCommandsList();
}
public string[] Call(string command, string arg)
@ -38,30 +40,6 @@ namespace BetterSecondBot.HttpServer
}
}
}
public class HTTPCommandsInterfaceGet : HTTPCommandsInterface
{
public HTTPCommandsInterfaceGet(SecondBot linktobot, SecondBotHttpServer sethttpserver) : base(linktobot, sethttpserver)
{
API_type = typeof(HTTP_commands_get);
LoadCommandsList();
}
}
public class HTTPCommandsInterfacePost : HTTPCommandsInterface
{
public HTTPCommandsInterfacePost(SecondBot linktobot, SecondBotHttpServer sethttpserver) : base(linktobot, sethttpserver)
{
API_type = typeof(HTTP_commands_post);
LoadCommandsList();
}
}
public abstract class HTTP_commands_get : HTTP_commands
{
}
public abstract class HTTP_commands_post : HTTP_commands
{
}
public abstract class HTTP_commands : API_interface
{

View file

@ -1,30 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterSecondBot.HttpServer.View.Basic
{
public class Status : HTTP_commands_get
{
public override string Helpfile { get { return "returns the status of the bot"; } }
public override string[] CallFunction(string[] args)
{
if (bot != null)
{
if (ArgsCheck(args) == true)
{
return new string[] { bot.GetStatus() };
}
else
{
return Failed("Incorrect number of args");
}
}
else
{
return Failed("No Bot");
}
}
}
}

View file

@ -0,0 +1,4 @@
.logo
{
width: 100%;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -0,0 +1,75 @@
$( document ).ready(function() {
$(".ajax").submit(function(e) {
e.preventDefault();
var form = $(this);
var url = form.attr('action');
var method = form.attr('method');
$.ajax({
type: method,
url: url,
data: form.serialize(),
success: function(data)
{
try
{
jsondata = JSON.parse(data);
var redirectdelay = 1500;
if(jsondata.hasOwnProperty('status'))
{
if(jsondata.hasOwnProperty('message'))
{
if(jsondata.status == true)
{
if(jsondata.message != "") alert_success(jsondata.message);
}
else
{
redirectdelay = 3500;
if(jsondata.message != "") alert_warning(jsondata.message);
}
}
if(jsondata.hasOwnProperty('redirect'))
{
setTimeout(function(){ $(location).attr('href', jsondata.redirect) }, redirectdelay);
}
}
else
{
alert_error("Reply from server is not vaild please reload the page and try again.");
}
}
catch(e)
{
alert_warning("Unable to process reply, please reload the page and try again!");
}
},
error: function (data)
{
alert_error(data);
}
});
});
});
function alert_success(smsg)
{
alert(smsg,"success");
}
function alert_error(smsg)
{
alert(smsg,"error");
}
function alert_warning(smsg)
{
alert(smsg,"warning");
}
function alert(smsg,alerttype)
{
$.notify({
// options
message: smsg
},{
// settings
type: alerttype
});
}

View file

@ -0,0 +1,2 @@
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"/>
<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css" rel="stylesheet" integrity="sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM" crossorigin="anonymous"/>

View file

@ -0,0 +1,3 @@
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

View file

@ -0,0 +1,33 @@
<html>
<head>
<title>[[html_title]] - [[html_title_after]]</title>
[[html_cs_top]]
<link rel="icon" type="image/png" href="[[url_base]]theme/images/favicon-32x32.png">
<link rel="stylesheet" type="text/css" href="[[url_base]]theme/css/site.css">
</head>
<body>
<div class="container">
<div class="row h-75 justify-content-center align-items-center">
<div class="col-6">
<div class="row justify-content-center">
<div class="col-6">
<a href="[[url_base]]"><img class="logo" src="[[url_base]]theme/images/logo.png" alt="logo"/></a>
</div>
</div>
<div class="row">
<div class="col-12">
[[page_content]]
</div>
</div>
</div>
</div>
</div>
[[html_js_bottom]]
<script type="text/javascript" src="[[url_base]]theme/js/site.js"></script>
<script type="text/javascript">
$( document ).ready(function() {
[[html_js_onready]]
});
</script>
</body>
</html>

View file

@ -0,0 +1,14 @@
<div class="card border border-success rounded">
<div class="card-body">
<h5 class="card-title">Bot webUI auth required</h5>
<form action="[[url_base]]ajax/login/start" method="post" class="ajax">
<div class="row mt-1">
<div class="col-8 offset-2">
<input name="password" class="form-control" type="password" placeholder="Please enter your auth code"/></div>
</div>
<div class="row mt-4">
<div class="col-12"><button class="btn btn-primary btn-block" type="submit">Login</button></div>
</div>
</form>
</div>
</div>

View file

@ -0,0 +1,59 @@
<html>
<head>
<title>[[html_title]] - [[html_title_after]]</title>
[[html_cs_top]]
<link rel="icon" type="image/png" href="[[url_base]]theme/images/favicon-32x32.png">
<link rel="stylesheet" type="text/css" href="[[url_base]]theme/css/site.css">
</head>
<body>
<div class="container-fluid">
<div class="row mt-1 justify-content-between">
<div class="col-1"><a href="[[url_base]]"><img class="logo" src="[[url_base]]theme/images/logo.png" alt="logo"/></a></div>
</div>
<div class="row">
<div class="col-2">
<div class="row">
<div class="col-12">
<ul class="nav nav-pills flex-column">
[[html_menu]]
</ul>
</div>
</div>
<div class="row align-items-end">
<div class="col-12">
<br/><br/>
<hr/>
</div>
</div>
<div class="row align-items-end">
<div class="col-5">
<a href="[[url_base]]login/logout"><button type="button" class="btn btn-block btn-secondary btn-sml">Logout</button></a>
</div>
</div>
</div>
<div class="col-10">
<div class="card">
<div class="card-header">
<div class="row mt-1 justify-content-between">
<div class="col-5">[[page_title]]</div>
<div class="col-1 justify-content-end">[[page_actions]]</div>
</div>
</div>
<div class="card-body">
[[page_content]]
</div>
</div>
</div>
</div>
</div>
[[html_js_bottom]]
<script type="text/javascript" src="[[url_base]]theme/js/site.js"></script>
<script type="text/javascript">
$( document ).ready(function() {
[[html_js_onready]]
});
</script>
</body>
</html>

79
CLI/HttpWebUi/webui.cs Normal file
View file

@ -0,0 +1,79 @@
using BetterSecondBotShared.Json;
using BetterSecondBotShared.Static;
using BSB.bottypes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
namespace BetterSecondBot.HttpWebUi
{
public class webui
{
JsonConfig siteconfig;
SecondBot bot;
public webui(JsonConfig setconfig,SecondBot setbot)
{
bot = setbot;
siteconfig = setconfig;
}
public KeyValuePair<string,byte[]> process(List<string> args,CookieCollection cookies)
{
Cookie logincookie = null;
foreach(Cookie C in cookies)
{
if(C.Name == "logincookie")
{
logincookie = C;
break;
}
}
if (logincookie == null)
{
if (args != null)
{
if (args.Count >= 1)
{
if (args[0] == "theme")
{
if (args.Count >= 2)
{
if (args[1] == "images")
{
return new KeyValuePair<string, byte[]>("image/png", helpers.ReadResourceFileBinary(Assembly.GetExecutingAssembly(), args[2]));
}
else if (args[1] == "css")
{
return new KeyValuePair<string, byte[]>("text/css", Encoding.UTF8.GetBytes(helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), args[2])));
}
else if (args[1] == "js")
{
return new KeyValuePair<string, byte[]>("text/javascript", Encoding.UTF8.GetBytes(helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), args[2])));
}
}
}
return new KeyValuePair<string, byte[]>("text/html", Encoding.UTF8.GetBytes(""));
}
}
string layout = helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "full.layout");
layout = layout.Replace("[[page_content]]", helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "login.block"));
layout = layout.Replace("[[url_base]]", siteconfig.HttpPublicUrlBase);
layout = layout.Replace("[[html_title_after]]", "Secondbot web UI");
layout = layout.Replace("[[html_title]]", siteconfig.userName);
layout = layout.Replace("[[html_cs_top]]", helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "cdn.css.layout"));
layout = layout.Replace("[[html_js_bottom]]", helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "cdn.js.layout"));
layout = layout.Replace("[[html_js_onready]]", "");
return new KeyValuePair<string, byte[]>("text/html", Encoding.UTF8.GetBytes(layout));
}
else
{
return new KeyValuePair<string, byte[]>("text/html", Encoding.UTF8.GetBytes(""));
}
}
}
}

View file

@ -16,7 +16,6 @@
<a href="[[SUBFOLDER]]Core.html" class="nav-item nav-link [[CoreActive]]">Main commands [Core]</a>
<a href="[[SUBFOLDER]]JSON.html" class="nav-item nav-link [[JSONActive]]">JSON [Bot Config file]</a>
<a href="[[SUBFOLDER]]RLVapi.html" class="nav-item nav-link [[RLVapiActive]]">Restrained Love API [RLV]</a>
<a href="[[SUBFOLDER]]HTTPget.html" class="nav-item nav-link [[HTTPgetActive]]">Get [HTTP]</a>
<a href="[[SUBFOLDER]]HTTPpost.html" class="nav-item nav-link [[HTTPpostActive]]">Post [HTTP]</a>
</div>
<div class="navbar-nav ml-auto">

View file

@ -2,11 +2,9 @@
using BetterSecondBotShared.IO;
using BetterSecondBotShared.Json;
using BetterSecondBotShared.logs;
using BSB;
using BetterSecondBotShared.Static;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
@ -21,25 +19,7 @@ namespace BetterSecondBot.WikiMake
protected SimpleIO io;
protected Dictionary<string,string> seen_command_names = new Dictionary<string, string>();
public static string ReadResourceFile(string filename)
{
try
{
var assembly = Assembly.GetExecutingAssembly();
string resourceName = assembly.GetManifestResourceNames().Single(str => str.EndsWith(filename));
string result = "";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
result = reader.ReadToEnd();
}
return result;
}
catch (Exception e)
{
return e.Message;
}
}
public string CreateRoot()
{
StringBuilder sb = new StringBuilder();
@ -297,8 +277,8 @@ namespace BetterSecondBot.WikiMake
{
io = shareio;
buildVersion = Version;
html_header = ReadResourceFile("wiki_header.txt");
html_footer = ReadResourceFile("wiki_footer.txt");
html_header = helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "wiki_header.txt");
html_footer = helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "wiki_footer.txt");
io.ChangeRoot("wiki");
io.ChangeRoot("wiki/files");
@ -317,14 +297,8 @@ namespace BetterSecondBot.WikiMake
api_reports.Add("RLVapi", new KeyValuePair<int, string>(RLVapi.ApiCommandsCount, "Basic"));
InterfaceWiki("RLVapi", RLVapi, true);
// HTTP get
BetterSecondBot.HttpServer.HTTPCommandsInterfaceGet http_get = new BetterSecondBot.HttpServer.HTTPCommandsInterfaceGet(null,null);
api_reports.Add("HTTPget", new KeyValuePair<int, string>(http_get.ApiCommandsCount, "Limited"));
InterfaceWiki("HTTPget", http_get);
// HTTP post
BetterSecondBot.HttpServer.HTTPCommandsInterfacePost http_post = new BetterSecondBot.HttpServer.HTTPCommandsInterfacePost(null,null);
BetterSecondBot.HttpServer.HTTPCommandsInterface http_post = new BetterSecondBot.HttpServer.HTTPCommandsInterface(null,null);
api_reports.Add("HTTPpost", new KeyValuePair<int, string>(http_post.ApiCommandsCount, "Limited"));
InterfaceWiki("HTTPpost", http_post);