dashboard fixes

This commit is contained in:
Nicholas Orlowsky 2024-08-29 15:33:55 -05:00
parent 517f9bffe7
commit 35dd581969
Signed by: nickorlow
GPG key ID: 838827D8C4611687
9 changed files with 119 additions and 47 deletions

BIN
bun.lockb Executable file

Binary file not shown.

View file

@ -7,7 +7,7 @@ import './CreateCruisePage.css';
export default function CreateCruisePage() { export default function CreateCruisePage() {
const [page, setPage] = useState('info'); const [page, setPage] = useState('info');
const [strat, setStrat] = useState<'raw-html' | 'react-js'>('raw-html'); const [strat, setStrat] = useState<'raw-html' | 'react-js' | 'raw-html-anthracite' | 'docker' | 'default'>('default');
const [owner, setOwner] = useState(''); const [owner, setOwner] = useState('');
const [repo, setRepo] = useState(''); const [repo, setRepo] = useState('');
const [name, setName] = useState(''); const [name, setName] = useState('');
@ -32,8 +32,8 @@ export default function CreateCruisePage() {
"serviceName": name, "serviceName": name,
"containerUrl": `ghcr.io/${owner}/${repo}`, "containerUrl": `ghcr.io/${owner}/${repo}`,
"namespaceId": search.get("namespaceId"), "namespaceId": search.get("namespaceId"),
"serviceUrl": hostUriInput, "serviceUrl": "https://"+hostUriInput,
"hostnammes": [] "hostnames": []
}) })
}).then((response)=> { }).then((response)=> {
if(response.status === 200) { if(response.status === 200) {
@ -84,10 +84,13 @@ export default function CreateCruisePage() {
<input value={name} onChange={(e)=>{setName(e.currentTarget.value)}}/> <input value={name} onChange={(e)=>{setName(e.currentTarget.value)}}/>
<h5 className={"label-text"}>How did you create your website?</h5> <h5 className={"label-text"}>Choose a Dockerfile template</h5>
<p className={"help-text"}>Don't see your technology/framework? Email me: <a href={"mailto:nws-support@nickorlow.com"}>nws-support@nickorlow.com</a></p> <p className={"help-text"}>Don't see your technology/framework? Email me: <a href={"mailto:nws-support@nickorlow.com"}>nws-support@nickorlow.com</a></p>
<select value={strat}> <select>
<option id={"raw-html"} onClick={()=>setStrat('raw-html')}>Raw HTML</option> <option hidden>Select a template</option>
<option id={"raw-html"} onClick={()=>setStrat('docker')}>I already have a Dockerfile in the root of my repository</option>
<option id={"raw-html"} onClick={()=>setStrat('raw-html-anthracite')}>Raw HTML (with Anthracite Web Server, created by Nick)</option>
<option id={"raw-html"} onClick={()=>setStrat('raw-html')}>Raw HTML (with NGINX)</option>
<option id={"react-js"} onClick={()=>setStrat('react-js')}>React JS</option> <option id={"react-js"} onClick={()=>setStrat('react-js')}>React JS</option>
</select> </select>
@ -113,7 +116,7 @@ export default function CreateCruisePage() {
setRepo(git_url.pathname.split('/')[2]) setRepo(git_url.pathname.split('/')[2])
} }
} catch (e) { } catch (e) {
alert('invalid github url') alert('Invalid GitHub URL. Should be of format https://github.com/owner/repo')
return; return;
} }
@ -121,7 +124,7 @@ export default function CreateCruisePage() {
try { try {
let url = new URL("https://"+hostUriInput); let url = new URL("https://"+hostUriInput);
} catch (e) { } catch (e) {
alert('invalid host url') alert('Invalid domain! Should be format subdomain.domain.tld. Don\'t include protocols in front (i.e https://)')
return; return;
} }
@ -138,10 +141,11 @@ export default function CreateCruisePage() {
page === 'scriptgen' && page === 'scriptgen' &&
<div> <div>
<h4>Copy & Paste the below into your terminal to add NWS deployment scripts to your webapp</h4> <h4>Copy & Paste the below into your terminal to add NWS deployment scripts to your webapp</h4>
<code lang={"shell"} style={{backgroundColor: "black", padding: 5, borderRadius: 10}}> <code lang={"shell"} style={{backgroundColor: "#bbbbbb", padding: 5, borderRadius: 10}}>
curl -s https://raw.githubusercontent.com/nickorlow/nws-ghactions-templates/main/add-nws.sh | bash -s {strat} {owner} {repo} curl -s https://raw.githubusercontent.com/nickorlow/nws-ghactions-templates/main/add-nws.sh | bash -s {strat} {owner} {repo}
</code> </code>
<br/><span>Ensure the script finishes running before continuing</span> <br/><span>Ensure the script finishes running before continuing</span>
<br/><span>For your security, you may view the source code of the script <a href="https://github.com/nickorlow/nws-ghactions-templates/blob/main/add-nws.sh" target="_blank">here</a></span>
<br/> <br/>
<button onClick={()=>setPage('framework-hostname')}>Back</button> <button onClick={()=>setPage('framework-hostname')}>Back</button>
@ -173,7 +177,7 @@ export default function CreateCruisePage() {
new URI("https://"+hostUriInput).subdomain().length > 0 && new URI("https://"+hostUriInput).subdomain().length > 0 &&
<div> <div>
<p>Type: CNAME</p> <p>Type: CNAME</p>
<p>Name: {new URI(hostUriInput).subdomain()} ({new URI(hostUriInput).hostname()})</p> <p>Name: {new URI("https://"+hostUriInput).subdomain()} ({new URI("https://"+hostUriInput).hostname()})</p>
<p>Value: entry.nws.nickorlow.com</p> <p>Value: entry.nws.nickorlow.com</p>
</div> </div>
} }
@ -184,9 +188,22 @@ export default function CreateCruisePage() {
{ {
page === 'done' && page === 'done' &&
<div> <div>
<h3>Welcome to NWS</h3> <br/> <h3>Welcome to NWS!</h3> <br/>
<p>Your site should be avaliable on NWS momentairly</p> <br/>
<p>It would be great if you could add the following to your website:</p><br/>
<code lang={"html"} style={{ backgroundColor: "#bbbbbb", padding: 5, borderRadius: 10 }}>
&lt;p&gt;Hosting provided by &lt;a href="https://nws.nickorlow.com"&gt;NWS&lt;/a&gt;&lt;/p&gt;
</code> <br/> <br/>
{ strat === "raw-html-anthracite" &&
<div>
<p>It would be great if you could add the following to your website as well:</p><br/>
<code lang={"html"} style={{ backgroundColor: "#bbbbbb", padding: 5, borderRadius: 10 }}>
&lt;p&gt;Powered by &lt;a href="https://github.com/nickorlow/anthracite"&gt;Anthracite Web Server&lt;/a&gt;&lt;/p&gt;
</code> <br/> <br/>
</div>
}
<button onClick={()=>{window.location.href="/dashboard"}}>Go to Dashboard</button> <br/> <button onClick={()=>{window.location.href="/dashboard"}}>Go to Dashboard</button> <br/>
<button onClick={()=>{window.location.href=hostUriInput}}>See my Site</button>
</div> </div>
} }
</div> </div>

View file

@ -4,9 +4,13 @@ import {
useGetAccountServices, useGetAccountServices,
useGetServicesInNamespace, useGetServicesInNamespace,
useLoggedInRedirect, useLoggedInRedirect,
useNWSAccount useNWSAccount,
useNWSAuthKey
} from "../nws-api/hooks"; } from "../nws-api/hooks";
import {useState} from "react"; import {
createNamespace
} from "../nws-api/calls"
import {useState, useEffect} from "react";
import {enableSSL} from "../nws-api/calls"; import {enableSSL} from "../nws-api/calls";
@ -16,25 +20,60 @@ export default function DashboardPage() {
let {setNs, services, ns} = useGetServicesInNamespace(); let {setNs, services, ns} = useGetServicesInNamespace();
let namespaces: Namespace[] = useGetAccountNamespaces(); let namespaces: Namespace[] = useGetAccountNamespaces();
const urlParams = new URLSearchParams(window.location.search);
return( return(
<div style={{minHeight: "100vh", padding: "50px"}}> <div style={{minHeight: "100vh", padding: "50px"}}>
<div className={"row"}> <div className={"row"}>
<p>I don't really know what I was on when I wrote this but a lot of things in the web ui are goofy, sorry about that.. :/ A new one is on its way.</p>
<h1 className={"col-md-10 col-12"}>Welcome to NWS, {account?.name}!</h1> <h1 className={"col-md-10 col-12"}>Welcome to NWS, {account?.name}!</h1>
<select className={"col-12 col-md-2"} defaultValue={"Select Namespace..."}> <div className={"col-12 col-md-2"}>
<option value="" disabled selected>Select Namespace...</option> <p>Namespace</p>
{ <select className="w-100">
namespaces.map((e)=>{ <option value="" disabled selected={!urlParams.has('namespace')}>Select Namespace...</option>
return <option onClick={(a)=>{setNs(e)}}>{e.name}</option> {
}) namespaces.map((e)=>{
} if (urlParams.get('namespace') === e.id && ns?.id != e.id)
<option value="" disabled>---</option> setNs(e);
<option value="create-ns">Create Namespace</option> return <option onClick={(a)=>{
</select> const url = new URL(window.location.toString());
url.searchParams.set('namespace', e.id);
window.history.pushState(null, '', url.toString());
setNs(e);
}} selected={urlParams.get('namespace') === e.id}>{e.name}</option>
})
}
</select>
<div>
<button className="w-100 p-0 mt-2" onClick={async () => {
let name = prompt("Enter a name for the namespace");
let rawSession: string | null = localStorage.getItem("session_key");
if (rawSession != null) {
let session: SessionKey = JSON.parse(rawSession);
let newNamespace = await createNamespace(name!, account!.id!, session);
const url = new URL(window.location.toString());
url.searchParams.set('namespace', newNamespace.id);
window.history.pushState(null, '', url.toString());
window.location.reload();
} else {
alert("Error creating namespace");
}
}}>
Create Namespace
</button>
</div>
</div>
</div> </div>
<hr/> <hr/>
<div className={"d-flex justify-content-between"}> <div className={"d-flex justify-content-between"}>
<h2>Container Deployment Services</h2> <h2>Container Deployment Services</h2>
<button onClick={(e) => {window.location.href = "/cruise/new?namespaceId="+ns!.id}}>Create Cruise Service</button> <button onClick={(e) => {
if (ns != null)
window.location.href = "/cruise/new?namespaceId="+ns!.id
else
alert("Please select a namespace!")
}}>Create Container Deployment</button>
</div> </div>
<div className={"row"}> <div className={"row"}>
@ -47,16 +86,19 @@ export default function DashboardPage() {
<p>{e.serviceId}</p> <p>{e.serviceId}</p>
{e.hostnames.map((host)=>{ {e.hostnames.map((host)=>{
return ( return (
<div className={"mb-2 p-2"}> <div className={"mb-2 p-2 d-flex justify-content-between"}>
<a href={host.hostname}>{host.hostname}</a> <a href={"http://"+host.hostname}>{host.hostname}</a>
{!host.isSslEnabled ? <a onClick={async () => { {!host.isSslEnabled ? <div><button onClick={async () => {
let rawSession: string | null = localStorage.getItem("session_key"); let rawSession: string | null = localStorage.getItem("session_key");
if (rawSession != null) { if (rawSession != null) {
let session: SessionKey = JSON.parse(rawSession); let session: SessionKey = JSON.parse(rawSession);
await enableSSL(account!.id!, e.serviceId, host.hostname, session); await enableSSL(account!.id!, e.serviceId, host.hostname, session);
alert(`SSL has been enabled on the hostname ${host.hostname}. It should be ready in 2-5 minutes.`);
// hack but whatever
window.location.reload();
} }
}}>Enable SSL</a> : <p>SSL is enabled!</p> }}>Enable SSL</button></div> : <p>SSL is enabled!</p>
} }
</div> </div>
) )

View file

@ -3,7 +3,7 @@ import React from "react";
export default function Footer() { export default function Footer() {
return ( return (
<footer className={"mt-2 p-3"} style={{backgroundColor: "#eee"}}> <footer className={"mt-2 p-3"} style={{backgroundColor: "#eee"}}>
<p>NWS is owned and operated by <a href={"http://nickorlow.com"}>Nicholas Orlowsky</a>.</p> <p>SMC is owned and operated by <a href={"http://nickorlow.com"}>Nicholas Orlowsky</a>.</p>
<p>Copyright © Nicholas Orlowsky {new Date().getFullYear()}</p> <p>Copyright © Nicholas Orlowsky {new Date().getFullYear()}</p>
</footer> </footer>
); );

View file

@ -37,18 +37,15 @@ export default function HomePage() {
<img src={NWSLogo} alt="nws-logo" style={{width: "70%"}}/> <img src={NWSLogo} alt="nws-logo" style={{width: "70%"}}/>
</div> </div>
<div className={"col-md-6 text-center d-flex justify-content-center flex-column align-items-center"}> <div className={"col-md-6 text-center d-flex justify-content-center flex-column align-items-center"}>
<h1>Nick Web Services</h1> <h1>Sharpe Mountain Compute</h1>
<p style={{maxWidth: 500}} className={"col-md-6 text-center"}> <p className={"col-md-6 text-center"}>
Nick Web Services is a hosting service based out of Sharpe Mountain Compute (fka Nick Web Services) is a reliable cloud compute provider. SMC is dedicated to achieving maximum uptime at a lower cost than traditional cloud compute providers.
Austin, Texas. It is committed
to achieving maximum uptime with better performance and a lower cost than any of the major cloud
services.
</p> </p>
</div> </div>
</div> </div>
<div className={"w-100 mt-2 flex justify-content-center align-content-center text-center"}> <div className={"w-100 mt-2 flex justify-content-center align-content-center text-center"}>
<h3><i>100% Uptime from 1/1/2023 - 11/8/2023</i></h3> <h3><i>100% Uptime from 1/1/2023 - 11/8/2023</i></h3>
<h4><a href={"https://youtu.be/WHdXWMFHuqA"} target="_blank" rel="noopener noreferrer">Watch the NWS Deployment Demo</a></h4> <h4><a href={"https://youtu.be/WHdXWMFHuqA"} target="_blank" rel="noopener noreferrer">Watch the SMC Deployment Demo</a></h4>
</div> </div>
<div style={{width: '75vw'}}> <div style={{width: '75vw'}}>
<hr/> <hr/>

View file

@ -96,9 +96,9 @@ export default function RegisterPage() {
</div> </div>
<div className={"reg-box"} style={{display: didRegister ? "flex" : "none"}}> <div className={"reg-box"} style={{display: didRegister ? "flex" : "none"}}>
<h3>Verify your E-Mail address.</h3> <h3>Successfully Registered!</h3>
<p>Please verify your E-Mail by clicking the link we sent to you at: <b>{email}</b></p> <p><a href="/login">Proceed to login</a></p>
</div> </div>
</div> </div>
); );

View file

@ -38,22 +38,22 @@ export default function UptimeComparisonCard(props: {uptime: UptimeRecord, isSer
</div> </div>
<hr className={" w-100"}/> <hr className={" w-100"}/>
<p className={"fw-bold d-lg-none"}>Uptime (Last Month)</p> <p className={"fw-bold d-lg-none"}>Uptime (Last Month)</p>
<div style={{height: 25, margin: 0}} className={"pt-2 pt-lg-0"}> <div style={{height: 25, margin: 0}} className={"pb-2 pt-lg-0"}>
<p className={getUptimeClass(props.uptime.uptimeMonth)}>{props.uptime.uptimeMonth}%</p> <p className={getUptimeClass(props.uptime.uptimeMonth)}>{props.uptime.uptimeMonth}%</p>
</div> </div>
<hr className={"d-lg-block d-none w-100"}/> <hr className={"d-lg-block d-none w-100"}/>
<p className={"fw-bold d-lg-none"}>Uptime ({new Date().getFullYear()} YTD)</p> <p className={"fw-bold d-lg-none"}>Uptime ({new Date().getFullYear()} YTD)</p>
<div style={{height: 25, margin: 0}} className={"pt-2 pt-lg-0"}> <div style={{height: 25, margin: 0}} className={"pb-2 pt-lg-0"}>
<p className={getUptimeClass(props.uptime.uptimeYtd)}>{props.uptime.uptimeYtd}%</p> <p className={getUptimeClass(props.uptime.uptimeYtd)}>{props.uptime.uptimeYtd}%</p>
</div> </div>
<hr className={"d-lg-block d-none w-100"}/> <hr className={"d-lg-block d-none w-100"}/>
<p className={"fw-bold d-lg-none"}>Avg Response Time (24hr)</p> <p className={"fw-bold d-lg-none"}>Avg Response Time (24hr)</p>
<div style={{height: 25, margin: 0}} className={"pt-2 pt-lg-0"}> <div style={{height: 25, margin: 0}} className={"pb-2 pt-lg-0"}>
<p className={getResponseTimeClass(props.uptime.averageResponseTime)}>{props.uptime.averageResponseTime}ms</p> <p className={getResponseTimeClass(props.uptime.averageResponseTime)}>{props.uptime.averageResponseTime}ms</p>
</div> </div>
<hr className={"d-lg-block d-none w-100"} /> <hr className={"d-lg-block d-none w-100"} />
<p className={"fw-bold d-lg-none"}>Current Status</p> <p className={"fw-bold d-lg-none"}>Current Status</p>
<div style={{height: 25, margin: 0}} className={"pt-2 pt-lg-0"}> <div style={{height: 25, margin: 0}} className={"pb-2 pt-lg-0"}>
<div className={`p-1 d-flex justify-content-start w-100`} > <div className={`p-1 d-flex justify-content-start w-100`} >
<p className={`fw-bold severity-label w-100 <p className={`fw-bold severity-label w-100

View file

@ -28,7 +28,7 @@ function Layout (props: {children: any}) {
<div> <div>
<header className={"w-100 sticky-top"}> <header className={"w-100 sticky-top"}>
<div className={"w-100"}> <div className={"w-100"}>
<Navbar sticky={"top"} expand="lg" className={"row justify-content-center m-0 p-0"} style={{backgroundColor: "#eee"}}> <Navbar sticky={"top"} expand="md" className={"row justify-content-center m-0 p-0"} style={{backgroundColor: "#eee"}}>
<div className={"row w-100"}> <div className={"row w-100"}>
<div className="row w-100 d-md-none d-sm-block"> <div className="row w-100 d-md-none d-sm-block">
<div className={"col-9"}> <div className={"col-9"}>

View file

@ -50,6 +50,22 @@ export async function getNamespaces(accountId: string, skey: SessionKey): Promis
return namespaces; return namespaces;
} }
export async function createNamespace(name: string, accountId: string, session: SessionKey): Promise<Namespace> {
let response: Response = await fetch('https://api-nws.nickorlow.com/namespaces', {
headers: {
'Content-Type': 'application/json',
Authorization: btoa(session.accountId + ":" + session.id)
},
method: 'POST',
body: JSON.stringify({
'name': name,
'ownerId': accountId
})
});
let namespace: Namespace = await response.json();
return namespace;
}
export async function enableSSL(accountId: string, serviceId: string, hostname: string, session: SessionKey) { export async function enableSSL(accountId: string, serviceId: string, hostname: string, session: SessionKey) {
await fetch('https://api-nws.nickorlow.com/'+accountId+'/service/'+serviceId+"/hosts/"+hostname+"/ssl", { await fetch('https://api-nws.nickorlow.com/'+accountId+'/service/'+serviceId+"/hosts/"+hostname+"/ssl", {
headers: { headers: {