tesses-webserver-extras/Tesses.WebServer.Swagme/Swagme.cs

199 lines
260 KiB
C#
Raw Normal View History

2022-09-30 08:27:12 +00:00
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Tesses.WebServer;
namespace Tesses.WebServer.Swagme
{
public sealed class SwagmeDocumentation
{
public SwagmeDocumentation(string url,string description,string expandedDesc) :this(description,expandedDesc)
{
Url = url;
}
public SwagmeDocumentation(string description,string expandedDesc) : this(description)
{
ExpandedDescription=expandedDesc;
}
public SwagmeDocumentation(string description) : this()
{
Description = description;
}
public SwagmeDocumentation()
{
Description = "";
Url="";
ExpandedDescription="";
}
public string Url {get;set;}
public string Description {get;set;}
public string ExpandedDescription {get;set;}
}
public sealed class SwagmeServer : IServer
{
public bool AbsoluteUrl {get;set;}
private string getUrl(ServerContext ctx,SwagmeDocumentation docs)
{
return AbsoluteUrl ? $"{ctx.OriginalUrlPath.TrimEnd('/')}/{docs.Url.TrimStart('/')}" : $"/{docs.Url.TrimStart('/')}";
}
#region Bootstrap
private const string bootstrap_css = @"@charset ""UTF-8"";/*!
* Bootstrap v5.2.1 (https://getbootstrap.com/)
* Copyright 2011-2022 The Bootstrap Authors
* Copyright 2011-2022 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)*/
:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-color-rgb:33,37,41;--bs-body-bg-rgb:255,255,255;--bs-font-sans-serif:system-ui,-apple-system,""Segoe UI"",Roboto,""Helvetica Neue"",""Noto Sans"",""Liberation Sans"",Arial,sans-serif,""Apple Color Emoji"",""Segoe UI Emoji"",""Segoe UI Symbol"",""Noto Color Emoji"";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,""Liberation Mono"",""Courier New"",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-2xl:2rem;--bs-border-radius-pill:50rem;--bs-link-color:#0d6efd;--bs-link-hover-color:#0a58ca;--bs-code-color:#d63384;--bs-highlight-bg:#fff3cd}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;border:0;border-top:1px solid;opacity:.25}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.1875em;background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:var(--bs-link-color);text-decoration:underline}a:hover{color:var(--bs-link-hover-color)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--b
/*# sourceMappingURL=bootstrap.min.css.map */";
private const string bootstrap_js = @"/*!
* Bootstrap v5.2.1 (https://getbootstrap.com/)\ * Copyright 2011-2022 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
!function(t,e){""object""==typeof exports&&""undefined""!=typeof module?module.exports=e(require(""@popperjs/core"")):""function""==typeof define&&define.amd?define([""@popperjs/core""],e):(t=""undefined""!=typeof globalThis?globalThis:t||self).bootstrap=e(t.Popper)}(this,(function(t){""use strict"";function e(t){if(t&&t.__esModule)return t;const e=Object.create(null,{[Symbol.toStringTag]:{value:""Module""}});if(t)for(const i in t)if(""default""!==i){const s=Object.getOwnPropertyDescriptor(t,i);Object.defineProperty(e,i,s.get?s:{enumerable:!0,get:()=>t[i]})}return e.default=t,Object.freeze(e)}const i=e(t),s=""transitionend"",n=t=>{let e=t.getAttribute(""data-bs-target"");if(!e||""#""===e){let i=t.getAttribute(""href"");if(!i||!i.includes(""#"")&&!i.startsWith("".""))return null;i.includes(""#"")&&!i.startsWith(""#"")&&(i=`#${i.split(""#"")[1]}`),e=i&&""#""!==i?i.trim():null}return e},o=t=>{const e=n(t);return e&&document.querySelector(e)?e:null},r=t=>{const e=n(t);return e?document.querySelector(e):null},a=t=>{t.dispatchEvent(new Event(s))},l=t=>!(!t||""object""!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),c=t=>l(t)?t.jquery?t[0]:t:""string""==typeof t&&t.length>0?document.querySelector(t):null,h=t=>{if(!l(t)||0===t.getClientRects().length)return!1;const e=""visible""===getComputedStyle(t).getPropertyValue(""visibility""),i=t.closest(""details:not([open])"");if(!i)return e;if(i!==t){const e=t.closest(""summary"");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},d=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains(""disabled"")||(void 0!==t.disabled?t.disabled:t.hasAttribute(""disabled"")&&""false""!==t.getAttribute(""disabled"")),u=t=>{if(!document.documentElement.attachShadow)return null;if(""function""==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?u(t.parentNode):null},_=()=>{},g=t=>{t.offsetHeight},f=()=>window.jQuery&&!document.body.hasAttribute(""data-bs-no-jquery"")?window.jQuery:null,p=[],m=()=>""rtl""===document.documentElement.dir,b=t=>{var e;e=()=>{const e=f();if(e){const i=t.NAME,s=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=s,t.jQueryInterface)}},""loading""===document.readyState?(p.length||document.addEventListener(""DOMContentLoaded"",(()=>{for(const t of p)t()})),p.push(e)):e()},v=t=>{""function""==typeof t&&t()},y=(t,e,i=!0)=>{if(!i)return void v(t);const n=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const s=Number.parseFloat(e),n=Number.parseFloat(i);return s||n?(e=e.split("","")[0],i=i.split("","")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let o=!1;const r=({target:i})=>{i===e&&(o=!0,e.removeEventListener(s,r),v(t))};e.addEventListener(s,r),setTimeout((()=>{o||a(e)}),n)},w=(t,e,i,s)=>{const n=t.length;let o=t.indexOf(e);return-1===o?!i&&s?t[n-1]:t[0]:(o+=i?1:-1,s&&(o=(o+n)%n),t[Math.max(0,Math.min(o,n-1))])},A=/[^.]*(?=\..*)\.|.*/,T=/\..*/,E=/::\d+$/,C={};let k=1;const L={mouseenter:""mouseover"",mouseleave:""mouseout""},I=new Set([""click"",""dblclick"",""mouseup"",""mousedown"",""contextmenu"",""mousewheel"",""DOMMouseScroll"",""mouseover"",""mouseout"",""mousemove"",""selectstart"",""selectend"",""keydown"",""keypress"",""keyup"",""orientationchange"",""touchstart"",""touchmove"",""touchend"",""touchcancel"",""pointerdown"",""pointermove"",""pointerup"",""pointerleave"",""pointercancel"",""gesturestart"",""gesturechange"",""gestureend"",""focus"",""blur"",""change"",""reset"",""select"",""submit"",""focusin"",""focusout"",""load"",""unload"",""beforeunload"",""resize"",""move"",""DOMContentLoaded"",""readystatechange"",""error"",""abort"",""scroll""]);function O(t,e){return e&&`${e}::${k++}`||t.uidEvent||k++}function S(t){const e=O(t);return t.uidEvent=e,C[e]=C[e]||{},C[e]}function D(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function N(t,e,i){const s=""string""==typeof e,n=s?i:e||i;let o=j(t);return I.has(o)||(o=t),[s,n
//# sourceMappingURL=bootstrap.min.js.map";
#endregion
private Dictionary<string,List<(SwagmeDocumentation doc,string method)>> docs=new Dictionary<string,List<(SwagmeDocumentation doc,string method)>>();
public SwagmeServer()
{
svr=new RouteServer();
svr.Add("/",Swagme);
}
private async Task Swagme(ServerContext ctx)
{
if(rerender) render(ctx);
await ctx.SendTextAsync(swagme_html);
}
private string swagme_html="";
private RouteServer svr;
private bool rerender=true;
private string generateStrong(string method)
{
if(method == "DELETE")
{
return "<strong class=\"text-danger\">GET</strong>";
}
if(method == "PUT")
{
return "<strong class=\"text-secondary\">GET</strong>";
}
if(method == "HEAD")
{
return "<strong class=\"text-info\" style=\"--bs-text-opacity: .5;\">GET</strong>";
}
if(method == "POST")
{
return "<strong class=\"text-warning\">GET</strong>";
}
return $"<strong class=\"text-primary\">{method}</strong>";
}
private void render(ServerContext e)
{
StringBuilder b= new StringBuilder();
b.Append($"<html><head><meta charset=\"UTF-8\"><meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"><meta name=\"viewport\" content=\"width=device-width initial-scale=1.0\"><title>Swagme</title><style>{bootstrap_css}</style></head><body><nav class=\"navbar\" style=\"background-color: #e3f2fd;\"><div class=\"container-fluid\"><a class=\"navbar-brand\" href=\"#\">Swagme</a></div></nav> <div class=\"container\"><br>");
b.Append("<div class=\"spapage\" id=\"index\"><h1>Groups</h1><p>Scroll down to see Frontend License</p><div class=\"list-group\">");
foreach(var item in docs)
{
b.Append($"<a href=\"#{HttpUtility.UrlEncode(item.Key)}\" class=\"list-group-item list-group-item-action\">{HttpUtility.HtmlEncode(item.Key)}</a>");
}
int accordianNum = 0;
int accordianItemNum =0;
b.Append("</div> <br> <div class=\"card\"><div class=\"card-header\">Frontend License</div><div class=\"card-body\"><h5 class=\"card-title\">Swagme uses bootstrap</h5><p class=\"card-text\">Bootstrap v5.2.1 (https://getbootstrap.com/)<br>Copyright 2011-2022 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)<br>Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) </p><a href=\"https://getbootstrap.com/\" class=\"btn btn-primary\">Get Bootstrap</a></div></div></div>");
foreach(var item in docs)
{
b.Append($"<div class=\"spapage\" id=\"{HttpUtility.UrlEncode(item.Key)}\"><h1>{HttpUtility.HtmlEncode(item.Key)}</h1><div class=\"accordion\" id=\"accordion{accordianNum}\">");
foreach(var item2 in item.Value)
{
b.Append($"<div class=\"accordion-item\"><h2 class=\"accordion-header\" id=\"heading{accordianItemNum}\"><button class=\"accordion-button collapsed\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#collapse{accordianItemNum}\" aria-expanded=\"false\" aria-controls=\"collapse{accordianItemNum}\">{generateStrong(item2.method)}&nbsp;&nbsp;{HttpUtility.HtmlEncode(getUrl(e,item2.doc))}</button></h2><div id=\"collapse{accordianItemNum}\" class=\"accordion-collapse collapse\" aria-labelledby=\"heading{accordianItemNum}\" data-bs-parent=\"#accordion{accordianNum}\"><div class=\"accordion-body\"><h4>{HttpUtility.HtmlEncode(item2.doc.Description)}</h4>{item2.doc.ExpandedDescription}<br><a href=\"./{HttpUtility.HtmlEncode(item2.doc.Url.TrimStart('/'))}\" class=\"btn btn-primary\">Execute</a></div></div></div>");
accordianItemNum++;
}
b.Append("</div></div>");
accordianNum++;
}
//we will do ui here
//spa script
b.Append("<script>addEventListener('hashchange', (event) => { setPage();});function setPage(){document.querySelectorAll('.spapage').forEach(e=>{e.hidden=true;});var hash=window.location.hash.replace('#','');if(hash.length === 0){hash=\"index\";}document.getElementById(hash).hidden=false;}setPage();</script>");
b.Append($"<script>{bootstrap_js}</script></body></html>");
rerender = false;
swagme_html=b.ToString();
}
public void Add(string url,HttpActionAsync hndlr,SwagmeDocumentation documentation,string method="GET",string group="No Group")
{
if(string.IsNullOrWhiteSpace(documentation.Url)) documentation.Url = url;
svr.Add(url,hndlr);
docs.Add(group,(documentation,"GET"));
rerender=true;
}
public void Add(string url,HttpAction hndlr,SwagmeDocumentation documentation,string method="GET",string group="No Group")
{
if(string.IsNullOrWhiteSpace(documentation.Url)) documentation.Url = url;
svr.Add(url,hndlr);
docs.Add(group,(documentation,method));
rerender=true;
}
public void Add(ShouldProcessFunc proc,HttpActionAsync hndlr,SwagmeDocumentation documentation,string method="GET",string group="No Group")
{
svr.Add(proc,hndlr);
docs.Add(group,(documentation,method));
rerender=true;
}
public void Add(ShouldProcessFunc proc,HttpAction hndlr,SwagmeDocumentation documentation,string method="GET",string group="No Group")
{
svr.Add(proc,hndlr);
docs.Add(group,(documentation,method));
rerender=true;
}
public void AddCors(ServerContext ctx)
{
svr.AddCors(ctx);
}
public async Task<bool> BeforeAsync(ServerContext ctx)
{
return await svr.BeforeAsync(ctx);
}
public async Task GetAsync(ServerContext ctx)
{
await svr.GetAsync(ctx);
}
public async Task PostAsync(ServerContext ctx)
{
await svr.PostAsync(ctx);
}
public async Task OptionsAsync(ServerContext ctx)
{
await svr.OptionsAsync(ctx);
}
public async Task OtherAsync(ServerContext ctx)
{
await svr.OtherAsync(ctx);
}
}
}