new uptime monitors + half-completed create container deployment flow

This commit is contained in:
Nicholas Orlowsky 2023-01-17 01:06:38 -06:00
parent ddb20ee17c
commit 847d11c841
No known key found for this signature in database
GPG key ID: 3845F78A73B14100
16 changed files with 550 additions and 84 deletions

View file

@ -2,20 +2,21 @@
<project version="4"> <project version="4">
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="03fafda4-e2c1-4602-a731-a2f96e84badd" name="Default Changelist" comment=""> <list default="true" id="03fafda4-e2c1-4602-a731-a2f96e84badd" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/src/components/CreateCruisePage.tsx" afterDir="false" /> <change afterPath="$PROJECT_DIR$/src/components/HomePage.tsx" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/components/DashboardPage.tsx" afterDir="false" /> <change afterPath="$PROJECT_DIR$/src/components/UptimeCard.css" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/components/Login.css" afterDir="false" /> <change afterPath="$PROJECT_DIR$/src/static/images/NWS_Logo_Transparent.png" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/components/LoginPage.tsx" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/components/NotFoundPage.tsx" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/components/RegisterPage.css" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/components/RegisterPage.tsx" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/components/VerifyPage.css" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/components/VerifyPage.tsx" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/nws-api/hooks.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/package-lock.json" beforeDir="false" afterPath="$PROJECT_DIR$/package-lock.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/App.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/App.css" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/App.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/App.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/CreateCruisePage.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/CreateCruisePage.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/DashboardPage.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/DashboardPage.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/Footer.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/Footer.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/StatusPage.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/StatusPage.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/UptimeCard.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/UptimeCard.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/index.tsx" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/nws-api/calls.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/nws-api/calls.ts" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/nws-api/calls.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/nws-api/calls.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/nws-api/hooks.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/nws-api/hooks.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/nws-api/types.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/nws-api/types.ts" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/nws-api/types.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/nws-api/types.ts" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
@ -27,9 +28,9 @@
<option name="RECENT_TEMPLATES"> <option name="RECENT_TEMPLATES">
<list> <list>
<option value="HTTP Request" /> <option value="HTTP Request" />
<option value="CSS File" />
<option value="TypeScript File" /> <option value="TypeScript File" />
<option value="TypeScript JSX File" /> <option value="TypeScript JSX File" />
<option value="CSS File" />
</list> </list>
</option> </option>
</component> </component>
@ -45,20 +46,18 @@
<option name="hideEmptyMiddlePackages" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" /> <option name="showLibraryContents" value="true" />
</component> </component>
<component name="PropertiesComponent"><![CDATA[{ <component name="PropertiesComponent">
"keyToString": { <property name="WebServerToolWindowFactoryState" value="false" />
"WebServerToolWindowFactoryState": "false", <property name="last_opened_file_path" value="$PROJECT_DIR$/src/static/images" />
"last_opened_file_path": "/home/nickorlow/programmming/personal/nws-site/src/components", <property name="list.type.of.created.stylesheet" value="CSS" />
"list.type.of.created.stylesheet": "CSS", <property name="nodejs_package_manager_path" value="npm" />
"nodejs_package_manager_path": "npm", <property name="ts.external.directory.path" value="$PROJECT_DIR$/node_modules/typescript/lib" />
"ts.external.directory.path": "/home/nickorlow/programmming/personal/nws-site/node_modules/typescript/lib", <property name="vue.rearranger.settings.migration" value="true" />
"vue.rearranger.settings.migration": "true" </component>
}
}]]></component>
<component name="RecentsManager"> <component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS"> <key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/src/components" />
<recent name="$PROJECT_DIR$/src/static/images" /> <recent name="$PROJECT_DIR$/src/static/images" />
<recent name="$PROJECT_DIR$/src/components" />
</key> </key>
</component> </component>
<component name="RunManager"> <component name="RunManager">
@ -82,6 +81,8 @@
<workItem from="1666469240565" duration="7361000" /> <workItem from="1666469240565" duration="7361000" />
<workItem from="1666543043382" duration="3699000" /> <workItem from="1666543043382" duration="3699000" />
<workItem from="1668047509596" duration="7310000" /> <workItem from="1668047509596" duration="7310000" />
<workItem from="1673378530233" duration="327000" />
<workItem from="1673538703809" duration="6147000" />
</task> </task>
<servers /> <servers />
</component> </component>

85
package-lock.json generated
View file

@ -20,12 +20,18 @@
"react-bootstrap": "^2.4.0", "react-bootstrap": "^2.4.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-markdown": "^8.0.3", "react-markdown": "^8.0.3",
"react-modal": "^3.16.1",
"react-router-dom": "^6.4.2", "react-router-dom": "^6.4.2",
"react-scripts": "5.0.0", "react-scripts": "5.0.0",
"react-tooltip": "^4.4.3", "react-tooltip": "^4.4.3",
"strip-markdown": "^5.0.0", "strip-markdown": "^5.0.0",
"typescript": "^4.5.5", "typescript": "^4.5.5",
"urijs": "^1.19.11",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
},
"devDependencies": {
"@types/react-modal": "^3.13.1",
"@types/urijs": "^1.19.19"
} }
}, },
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
@ -3634,6 +3640,15 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"node_modules/@types/react-modal": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.13.1.tgz",
"integrity": "sha512-iY/gPvTDIy6Z+37l+ibmrY+GTV4KQTHcCyR5FIytm182RQS69G5ps4PH2FxtC7bAQ2QRHXMevsBgck7IQruHNg==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/react-transition-group": { "node_modules/@types/react-transition-group": {
"version": "4.4.5", "version": "4.4.5",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz",
@ -3708,6 +3723,12 @@
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
"integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ=="
}, },
"node_modules/@types/urijs": {
"version": "1.19.19",
"resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.19.tgz",
"integrity": "sha512-FDJNkyhmKLw7uEvTxx5tSXfPeQpO0iy73Ry+PmYZJvQy0QIWX8a7kJ4kLWRf+EbTPJEPDSgPXHaM7pzr5lmvCg==",
"dev": true
},
"node_modules/@types/warning": { "node_modules/@types/warning": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz",
@ -7298,6 +7319,11 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1" "url": "https://github.com/sindresorhus/execa?sponsor=1"
} }
}, },
"node_modules/exenv": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
"integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw=="
},
"node_modules/exit": { "node_modules/exit": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
@ -13942,6 +13968,24 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
}, },
"node_modules/react-modal": {
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz",
"integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==",
"dependencies": {
"exenv": "^1.2.0",
"prop-types": "^15.7.2",
"react-lifecycles-compat": "^3.0.0",
"warning": "^4.0.3"
},
"engines": {
"node": ">=8"
},
"peerDependencies": {
"react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18",
"react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18"
}
},
"node_modules/react-refresh": { "node_modules/react-refresh": {
"version": "0.11.0", "version": "0.11.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
@ -16070,6 +16114,11 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/urijs": {
"version": "1.19.11",
"resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz",
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
},
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -19677,6 +19726,15 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"@types/react-modal": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.13.1.tgz",
"integrity": "sha512-iY/gPvTDIy6Z+37l+ibmrY+GTV4KQTHcCyR5FIytm182RQS69G5ps4PH2FxtC7bAQ2QRHXMevsBgck7IQruHNg==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/react-transition-group": { "@types/react-transition-group": {
"version": "4.4.5", "version": "4.4.5",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz",
@ -19751,6 +19809,12 @@
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
"integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ=="
}, },
"@types/urijs": {
"version": "1.19.19",
"resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.19.tgz",
"integrity": "sha512-FDJNkyhmKLw7uEvTxx5tSXfPeQpO0iy73Ry+PmYZJvQy0QIWX8a7kJ4kLWRf+EbTPJEPDSgPXHaM7pzr5lmvCg==",
"dev": true
},
"@types/warning": { "@types/warning": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz",
@ -22378,6 +22442,11 @@
"strip-final-newline": "^2.0.0" "strip-final-newline": "^2.0.0"
} }
}, },
"exenv": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
"integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw=="
},
"exit": { "exit": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
@ -26949,6 +27018,17 @@
} }
} }
}, },
"react-modal": {
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz",
"integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==",
"requires": {
"exenv": "^1.2.0",
"prop-types": "^15.7.2",
"react-lifecycles-compat": "^3.0.0",
"warning": "^4.0.3"
}
},
"react-refresh": { "react-refresh": {
"version": "0.11.0", "version": "0.11.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
@ -28497,6 +28577,11 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"urijs": {
"version": "1.19.11",
"resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz",
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
},
"util-deprecate": { "util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View file

@ -15,11 +15,13 @@
"react-bootstrap": "^2.4.0", "react-bootstrap": "^2.4.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-markdown": "^8.0.3", "react-markdown": "^8.0.3",
"react-modal": "^3.16.1",
"react-router-dom": "^6.4.2", "react-router-dom": "^6.4.2",
"react-scripts": "5.0.0", "react-scripts": "5.0.0",
"react-tooltip": "^4.4.3", "react-tooltip": "^4.4.3",
"strip-markdown": "^5.0.0", "strip-markdown": "^5.0.0",
"typescript": "^4.5.5", "typescript": "^4.5.5",
"urijs": "^1.19.11",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
"resolutions": { "resolutions": {
@ -49,5 +51,9 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
},
"devDependencies": {
"@types/react-modal": "^3.13.1",
"@types/urijs": "^1.19.19"
} }
} }

View file

@ -16,7 +16,7 @@ input {
} }
button { button {
border-radius: 10px; border-radius: 10px !important;
border-width: 0px; border-width: 0px;
background-color: cornflowerblue; background-color: cornflowerblue;

View file

@ -1,8 +1,181 @@
export default function CreateCruisePage() { import {useState} from "react";
return ( import URI from "urijs";
<div> import {Namespace} from "../nws-api/types";
<h1>Create Cruise</h1> import {useNWSAccount, useNWSAuthKey} from "../nws-api/hooks";
export default function CreateCruisePage() {
const [page, setPage] = useState('info');
const [strat, setStrat] = useState<'raw-html' | 'react-js'>('raw-html');
const [owner, setOwner] = useState('');
const [repo, setRepo] = useState('');
const [name, setName] = useState('');
const [gitUriInput, setGUI] = useState('');
const [hostUriInput, setHUI] = useState('');
const authKey = useNWSAuthKey();
const acct = useNWSAccount();
function deploy() {
fetch("https://api-nws.nickorlow.com/Services/" + acct!.id + "/service",
{
method: 'POST',
headers: {
"Authorization": authKey
},
body: JSON.stringify({
"serviceName": name,
"containerUrl": `ghcr.io/${owner}/${repo}`,
"namespaceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"serviceUrl": hostUriInput,
})
}).then((response)=> {
if(response.status === 200) {
}
}).catch((ex) =>{
alert(ex)
});
}
return (
<div className={"App"}>
<h1>Create Container Deployment</h1>
<div style={{width: "75vw"}}>
{
page === 'info' &&
<div>
<h3>Some information before we get started:</h3>
<ul>
<li>NWS is free to use</li>
<li>Currently, your DNS provider must support DNS flattening if you intend to point your root domain (e.g. nickorlow.com) to NWS. Subdomains should work fine though. (Cloudflare, Route 53, and Pagely). (Moving to Cloudflare is pretty easy)</li>
<li>Through the Web UI, you may only add one domain name. If you need to add more, <a href={"mailto:nws-support@nickorlow.com"}>contact me</a></li>
<li>NWS does not guarantee any uptime</li>
<li>NWS is run by a college student with little free time, support may reflect this</li>
<li>This platform is very early in development. It may require you to have some technical
knowledge.
</li>
<li>NWS may cease operations in the event of a widespread viral infection transmitted via
bites or contact with bodily fluids that causes human corpses to reanimate and seek to
consume living human flesh, blood, brain or nerve tissue and is likely to result in the
fall of organized civilization.
</li>
</ul>
<button onClick={()=>setPage('framework-hostname')}>I understand, continue</button>
</div>
}
{
page === 'framework-hostname' &&
<div>
<h5>What is this deployment's name?</h5>
<i>May only be lowercase letters and dashes, max 20 chars</i>
<br/>
<input value={name} onChange={(e)=>{setName(e.currentTarget.value)}}/>
<br/>
<h5>How did you create your website?</h5>
<p>Don't see your technology/framework? Email me: <a href={"mailto:nws-support@nickorlow.com"}>nws-support@nickorlow.com</a></p>
<select value={strat}>
<option id={"raw-html"} onClick={()=>setStrat('raw-html')}>Raw HTML</option>
<option id={"react-js"} onClick={()=>setStrat('react-js')}>React JS</option>
</select>
<br/>
<h5>What is the url of the github repo where your code is hosted? (e.g. https://github.com/nickorlow/personal-site)</h5>
<p>Other git hosting providers are not currently supported through the Web UI</p>
<p>The repo must be public to create it through the Web UI</p>
<input value={gitUriInput} onInput={(e)=>{setGUI(e.currentTarget.value)}}/>
<br/>
<h5>What domain name will you use with your website? (e.g. nickorlow.com)</h5>
<input value={hostUriInput} onChange={(e)=>{setHUI(e.currentTarget.value)}}/>
<br/>
<button onClick={()=>{
try {
let git_url = new URL(gitUriInput);
if (git_url.host !== 'github.com') {
alert('Only github is supported!')
return;
} else {
console.log(git_url.pathname.split('/'))
setOwner(git_url.pathname.split('/')[1])
setRepo(git_url.pathname.split('/')[2])
}
} catch (e) {
alert('invalid github url')
return;
}
try {
let url = new URL("https://"+hostUriInput);
} catch (e) {
alert('invalid host url')
return;
}
if(!/^[a-z-]+$/.test(name) || name.length > 20 || name.length == 0) {
alert('may only be lowercase and dashes and under 20 chars')
return;
}
setHUI("https://"+hostUriInput);
setPage('scriptgen')
}}>Continue</button>
<button onClick={()=>{setPage('info')}}>Back</button>
</div>
}
{
page === 'scriptgen' &&
<div>
<h4>Copy & Paste the below into your terminal to add NWS deployment scripts to your webapp</h4>
<code style={{backgroundColor: "black", padding: 5, borderRadius: 10}}> curl -s https://raw.githubusercontent.com/nickorlow/nws-ghactions-templates/main/add-nws.sh | bash -s {strat} {owner} {repo}</code>
<br/><span>Ensure the script finishes running before continuing</span>
<br/>
<button onClick={()=>{
deploy();
setPage('dns');
}}>Continue</button>
<button onClick={()=>setPage('framework-hostname')}>Back</button>
</div>
}
{
page === 'dns' &&
<div>
<h4>Add the following DNS entry to {new URI(hostUriInput).hostname()}</h4>
{
new URI(hostUriInput).subdomain().length == 0 &&
<div>
<p>If your DNS provider is:</p>
<ul>
<li>Cloudflare</li>
<li>Route 53</li>
<li>Pagely</li>
</ul>
<p>Type: CNAME</p>
<p>Name: @ ({hostUriInput})</p>
<p>Value: entry.nws.nickorlow.com</p>
</div>
}
{
new URI(hostUriInput).subdomain().length > 0 &&
<div>
<p>Type: CNAME</p>
<p>Name: {new URI(hostUriInput).subdomain()} ({new URI(hostUriInput).hostname()})</p>
<p>Value: entry.nws.nickorlow.com</p>
</div>
}
<br/>
<button onClick={()=>setPage('done')}>Finish Setup</button>
</div>
}
{
page === 'done' &&
<div>
<h3>Welcome to NWS</h3> <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

@ -1,22 +1,42 @@
import {Account, Service} from "../nws-api/types"; import {Account, Namespace, Service} from "../nws-api/types";
import {useGetAccountServices, useLoggedInRedirect, useNWSAccount} from "../nws-api/hooks"; import {
useGetAccountNamespaces,
useGetAccountServices,
useGetServicesInNamespace,
useLoggedInRedirect,
useNWSAccount
} from "../nws-api/hooks";
import {useState} from "react";
export default function DashboardPage() { export default function DashboardPage() {
useLoggedInRedirect(); useLoggedInRedirect();
let account: Account | undefined = useNWSAccount(); let account: Account | undefined = useNWSAccount();
let services: Service[] = useGetAccountServices(); let {setNs, services, ns} = useGetServicesInNamespace();
let namespaces: Namespace[] = useGetAccountNamespaces();
return( return(
<div style={{minHeight: "100vh", padding: "50px"}}> <div style={{minHeight: "100vh", padding: "50px"}}>
<h1>Welcome to NWS, {account?.name}!</h1> <div className={"row"}>
<h1 className={"col-md-10 col-12"}>Welcome to NWS, {account?.name}!</h1>
<select className={"col-12 col-md-2"} defaultValue={"Select Namespace..."}>
<option value="" disabled selected>Select Namespace...</option>
{
namespaces.map((e)=>{
return <option onClick={(a)=>{setNs(e)}}>{e.name}</option>
})
}
<option value="" disabled>---</option>
<option value="create-ns">Create Namespace</option>
</select>
</div>
<hr/> <hr/>
<div className={"d-flex justify-content-between"}> <div className={"d-flex justify-content-between"}>
<h2>Your NWS Cruise Services</h2> <h2>Container Deployment Services</h2>
<button onClick={(e) => {window.location.href = "/cruise/new"}}>Create Cruise Service</button> <button onClick={(e) => {window.location.href = "/cruise/new?namespaceId="+ns!.id}}>Create Cruise Service</button>
</div> </div>
{/*<h2>Your NWS Write™ Blogs</h2>*/}
<div className={"row"}> <div className={"row"}>
{services.map((e)=>{ {services.map((e)=>{
return ( return (
<div className={"col-4"} style={{ padding: 5}}> <div className={"col-4"} style={{ padding: 5}}>
@ -24,12 +44,10 @@ export default function DashboardPage() {
<h3>{e.serviceName}</h3> <h3>{e.serviceName}</h3>
<p><b>Application Id</b></p> <p><b>Application Id</b></p>
<p>{e.serviceId}</p> <p>{e.serviceId}</p>
<p><b>Deployment Key</b></p>
<a href={"#regen"}>Regenerate Deploy Key</a>
</div> </div>
</div>); </div>);
})} })}
</div> </div>
</div> </div>
); );
} }

View file

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

View file

@ -0,0 +1,66 @@
import NWSLogo from "../static/images/NWS_Logo.png";
import UptimeCard from "./UptimeCard";
import IncidentCard from "./IncidentCard";
import Footer from "./Footer";
import React, {useEffect, useState} from "react";
import {Incident, UptimeResponse} from "../nws-api/types";
import {getIncidents, getUptime} from "../nws-api/calls";
import "../App.css";
export default function HomePage() {
const [uptime, setUptime] = useState<UptimeResponse>({datacenters: [], services: [], competitors: [], lastUpdated: ""});
const [incidents, setIncidents] = useState<Incident[]>([]);
const fetchUptime = async () => {
let resp: UptimeResponse = await getUptime();
setUptime(resp);
}
const fetchIncidents = async () => {
let resp: Incident[] = await getIncidents();
setIncidents(resp);
}
useEffect(() => {
fetchUptime();
fetchIncidents();
}, []);
return(
<div className="App">
<div className={"w-100 row"}>
<div className={"col-md-6 d-flex justify-content-center flex-column align-items-center"}>
<img src={NWSLogo} alt="nws-logo" style={{width: "70%"}}/>
</div>
<div className={"col-md-6 text-center d-flex justify-content-center flex-column align-items-center"}>
<h1>Nick Web Services</h1>
<p style={{maxWidth: 500}} className={"col-md-6 text-center"}>
Nick Web Services is a hosting service based out of
Austin, Texas. It is committed
to achieving maximum uptime with better performance and a lower cost than any of the major cloud
services.
</p>
</div>
</div>
<div style={{width: '75vw'}}>
<hr/>
</div>
<div className={"text-left row"} style={{width: '75vw'}}>
<h2>Compare us to our competitors</h2>
<p>Last updated at {new Date(uptime.lastUpdated).toLocaleString()}</p>
<div className={"col-12 row w-100 m-0"}>
{uptime.competitors.sort((a,b)=>{return b.uptimeMonth === a.uptimeMonth ? (a.name === "NWS" ? -1000 : b.name.localeCompare(a.name)) : b.uptimeMonth - a.uptimeMonth}).map((e) => {
return (
<UptimeCard uptime={e} isService={false}/>
);
})}
</div>
</div>
</div>
);
}

View file

@ -10,7 +10,7 @@ import "../App.css";
export default function StatusPage() { export default function StatusPage() {
const [uptime, setUptime] = useState<UptimeResponse>({datacenters: [], services: [], lastUpdated: ""}); const [uptime, setUptime] = useState<UptimeResponse>({datacenters: [], services: [], competitors: [], lastUpdated: ""});
const [incidents, setIncidents] = useState<Incident[]>([]); const [incidents, setIncidents] = useState<Incident[]>([]);
const fetchUptime = async () => { const fetchUptime = async () => {
@ -29,35 +29,15 @@ export default function StatusPage() {
}, []); }, []);
return( return(
<div className="App"> <div className="App" style={{padding: 20}}>
<div className={"w-100 row"}>
<div className={"col-md-6 d-flex justify-content-center flex-column align-items-center"}>
<img src={NWSLogo} alt="nws-logo" style={{width: "70%"}}/>
</div>
<div className={"col-md-6 text-center d-flex justify-content-center flex-column align-items-center"}>
<h1>Nick Web Services</h1>
<p style={{maxWidth: 500}} className={"col-md-6 text-center"}>
Nick Web Services is a hosting service based out of
Austin, Texas. It is committed
to achieving maximum uptime with better performance and a lower cost than any of the major cloud
services.
</p>
</div>
</div>
<div style={{width: '75vw'}}>
<hr/>
</div>
<div className={"text-left row"} style={{width: '75vw'}}> <div className={"text-left row"} style={{width: '75vw'}}>
<h2>NWS System Status</h2> <h1>NWS System Status</h1>
<p>Last updated at {new Date(uptime.lastUpdated).toLocaleString()}</p> <p>Last updated at {new Date(uptime.lastUpdated).toLocaleString()}</p>
<div className={"col-md-6 col-12"}> <div className={"col-md-6 col-12"}>
<h3>Service Status</h3> <h3>Service Status</h3>
{uptime.services.map((e) => { {uptime.services.map((e) => {
return ( return (
<UptimeCard uptime={e}/> <UptimeCard uptime={e} isService={true}/>
); );
})} })}
</div> </div>
@ -65,7 +45,7 @@ export default function StatusPage() {
<h3>Datacenter Status</h3> <h3>Datacenter Status</h3>
{uptime.datacenters.map((e) => { {uptime.datacenters.map((e) => {
return ( return (
<UptimeCard uptime={e}/> <UptimeCard uptime={e} isService={false}/>
); );
})} })}
</div> </div>

View file

@ -0,0 +1,28 @@
.uptime-lnk {
font-weight: bolder;
color: #2f2f2f;
text-decoration: none;
transition: .5s;
cursor: pointer;
}
.uptime-lnk:hover {
color: #e08b0d;
}
.uptime-modal {
border-radius: 20px;
border-color: transparent;
background-color: #eee;
padding: 20px;
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
width: 500px;
max-width: 100vw;
}

View file

@ -1,29 +1,52 @@
import {UptimeRecord} from "../nws-api/types"; import {UptimeRecord} from "../nws-api/types";
import React from "react"; import React, {useState} from "react";
import '../App.css'; import '../App.css';
import "./UptimeCard.css"
import Modal from "react-modal";
export default function UptimeCard(props: {uptime: UptimeRecord}) { export default function UptimeCard(props: {uptime: UptimeRecord, isService: boolean}) {
const [isModalOpen, setModalOpen] = useState(false);
return( return(
<div className={"nws-card row mb-2"} style={{maxWidth: '100%'}}> <div className={"nws-card row mb-2 m-0"} style={{maxWidth: '100%'}}>
{props.uptime.url != null && <h3 className={"col-md-9 col-12"}><a href={props.uptime.url} style={{textDecoration: "none"}}>{props.uptime.name}</a></h3>} <h3 className={"col-md-9 col-12 uptime-lnk"} onClick={()=>setModalOpen(true)}>{props.uptime.name}</h3>
{props.uptime.url == null && <h3 className={"col-md-9 col-12"}>{props.uptime.name}</h3>}
<div className={`col-md-3 col-12 d-flex d-md-none justify-content-start`}> <div className={`col-md-3 col-12 d-flex d-md-none justify-content-start`}>
<p className={`fw-bold severity-label <p className={`fw-bold severity-label w-100
${props.uptime.isUp ? 'low' : (props.uptime.undergoingMaintenance ? 'med' : 'high')}-severity`} ${props.uptime.isUp ? 'low' : (props.uptime.undergoingMaintenance ? 'med' : 'high')}-severity`}
style={{width: "max-content"}}> >
{props.uptime.isUp ? 'Up' : (props.uptime.undergoingMaintenance ? 'Maintenance' : 'Down')} {props.uptime.isUp ? 'Up' : (props.uptime.undergoingMaintenance ? 'Maintenance' : 'Down')}
</p> </p>
</div> </div>
<div className={`d-md-flex col-md-3 col-12 d-none justify-content-end`}> <div className={`d-md-flex col-md-3 col-12 d-none justify-content-end`}>
<p className={`fw-bold severity-label <p className={`fw-bold severity-label
${props.uptime.isUp ? 'low' : (props.uptime.undergoingMaintenance ? 'med' : 'high')}-severity`} ${props.uptime.isUp ? 'low' : (props.uptime.undergoingMaintenance ? 'med' : 'high')}-severity`}
style={{width: "max-content"}}> style={{width: "max-content", height: 'max-content'}}>
{props.uptime.isUp ? 'Up' : (props.uptime.undergoingMaintenance ? 'Maintenance' : 'Down')} {props.uptime.isUp ? 'Up' : (props.uptime.undergoingMaintenance ? 'Maintenance' : 'Down')}
</p> </p>
</div> </div>
<p className={"col-md-12 col-12"}><b>Last Month Uptime:</b> {props.uptime.uptimeMonth}%</p> <p className={"col-md-12 col-12"}><b>Last Month Uptime:</b> {props.uptime.uptimeMonth}%</p>
<p className={"col-md-6 col-12"}><b>All Time Uptime:</b> {props.uptime.uptimeAllTime}%</p> <p className={"col-md-6 col-12"}><b>{new Date().getFullYear()} Uptime:</b> {props.uptime.uptimeYtd}%</p>
<p className={"col-md-6 col-12"}><b>Avg Response Time:</b> {props.uptime.averageResponseTime}ms</p>
<Modal className={"uptime-modal"} isOpen={isModalOpen}>
<div className={"mb-3"}>
<h1 className={"mb-0"}>{props.uptime.name}</h1>
{props.uptime.url && <p>(<a href={props.uptime.url}>{props.uptime.url}</a>)</p>}
</div>
<div className={"mb-3"}>
<p>Monitoring since {props.uptime.monitorStart}</p>
<p><b>{new Date().getFullYear()} Uptime (YTD):</b> {props.uptime.uptimeYtd}%</p>
<p><b>Last Month Uptime:</b> {props.uptime.uptimeMonth}%</p>
<p><b>All-Time Uptime:</b> {props.uptime.uptimeAllTime}%</p>
<p><b>Average Response Time:</b> {props.uptime.averageResponseTime}ms</p>
</div>
{
props.isService &&
<div className={"mb-3"}>
<i>Note that the uptime and performance of services hosted on NWS may be affected by factors not controlled by NWS such as bad bad optimization or buggy software.</i>
</div>
}
<button className={"w-100"} onClick={()=>setModalOpen(false)}>Close</button>
</Modal>
</div> </div>
); );
} }

View file

@ -11,7 +11,7 @@ import 'bootstrap/dist/css/bootstrap.min.css';
import UptimeCard from "./components/UptimeCard"; import UptimeCard from "./components/UptimeCard";
import Footer from "./components/Footer"; import Footer from "./components/Footer";
import {Nav, Navbar, NavbarBrand, NavDropdown} from "react-bootstrap"; import {Nav, Navbar, NavbarBrand, NavDropdown} from "react-bootstrap";
import NWSLogo from "./static/images/NWS_Logo.png"; import NWSLogo from "./static/images/NWS_Logo_Transparent.png";
import Blogs from "./components/Blogs"; import Blogs from "./components/Blogs";
import NotFoundPage from "./components/NotFoundPage"; import NotFoundPage from "./components/NotFoundPage";
import LoginPage from "./components/LoginPage"; import LoginPage from "./components/LoginPage";
@ -19,11 +19,12 @@ import RegisterPage from "./components/RegisterPage";
import VerifyPage from "./components/VerifyPage"; import VerifyPage from "./components/VerifyPage";
import DashboardPage from "./components/DashboardPage"; import DashboardPage from "./components/DashboardPage";
import CreateCruisePage from "./components/CreateCruisePage"; import CreateCruisePage from "./components/CreateCruisePage";
import HomePage from "./components/HomePage";
function Layout (props: {children: any}) { function Layout (props: {children: any}) {
return ( return (
<div> <div>
<Navbar sticky={"top"} style={{backgroundColor: "#eee", paddingLeft: 100, paddingRight: 100}} className={"row"}> <Navbar sticky={"top"} style={{height: "8vh", backgroundColor: "#eee", paddingLeft: "5vw", paddingRight: "5vw", maxWidth:"100%"}} className={"row m-0"}>
<div className={"col-10"}> <div className={"col-10"}>
<NavbarBrand> <NavbarBrand>
<img src={NWSLogo} alt="nws-logo" style={{width: 120}}/> <img src={NWSLogo} alt="nws-logo" style={{width: 120}}/>
@ -38,7 +39,7 @@ function Layout (props: {children: any}) {
{/*<NavLink className={"nav-lnk"} to={"/blogs"}>*/} {/*<NavLink className={"nav-lnk"} to={"/blogs"}>*/}
{/* Blog*/} {/* Blog*/}
{/*</NavLink>*/} {/*</NavLink>*/}
<div className={"col-2"}> <div className={"col-2 d-none d-md-block"}>
{ localStorage.getItem("session_key") === null && { localStorage.getItem("session_key") === null &&
( (
<NavLink className={"nav-lnk"} to={"/login"}> <NavLink className={"nav-lnk"} to={"/login"}>
@ -62,7 +63,9 @@ function Layout (props: {children: any}) {
} }
</div> </div>
</Navbar> </Navbar>
{props.children} <div style={{minHeight: "92vh"}}>
{props.children}
</div>
<Footer/> <Footer/>
</div> </div>
); );
@ -73,7 +76,7 @@ const router = createBrowserRouter([
path: "/", path: "/",
element: element:
<Layout> <Layout>
<StatusPage/> <HomePage/>
</Layout> </Layout>
}, },
{ {

View file

@ -1,4 +1,4 @@
import {Blog, Incident, Service, SessionKey, UptimeResponse} from "./types"; import {Blog, Incident, Namespace, Service, SessionKey, UptimeResponse} from "./types";
export async function getUptime(): Promise<UptimeResponse> { export async function getUptime(): Promise<UptimeResponse> {
let response: Response = await fetch('https://api-nws.nickorlow.com/uptime'); let response: Response = await fetch('https://api-nws.nickorlow.com/uptime');
@ -40,4 +40,12 @@ export async function getSessionKey(accountId: string, password: string): Promis
return sessionKey; return sessionKey;
} }
export async function getNamespaces(accountId: string, skey: SessionKey): Promise<Namespace[]> {
let response: Response = await fetch('https://api-nws.nickorlow.com/'+accountId+'/namespaces', {
headers: {
Authorization: skey.id
}
});
let namespaces: Namespace[] = await response.json();
return namespaces;
}

View file

@ -1,5 +1,5 @@
import {useEffect, useState} from "react"; import {useEffect, useState} from "react";
import {Account, Service, SessionKey} from "./types"; import {Account, Namespace, Service, SessionKey} from "./types";
export function useNonLoggedInRedirect() { export function useNonLoggedInRedirect() {
useEffect(()=>{ useEffect(()=>{
@ -58,6 +58,72 @@ export function useGetAccountServices() {
return services; return services;
} }
export function useGetAccountNamespaces() {
const [namespaces, setNamespaces] = useState<Namespace[]>([]);
useEffect(() => {
let rawSession: string | null = localStorage.getItem("session_key");
if(rawSession != null) {
let session: SessionKey = JSON.parse(rawSession);
fetch("https://api-nws.nickorlow.com/Account/" + session.accountId + "/namespaces",
{
headers: {
"Authorization": btoa(session.accountId + ":" + session.id)
}
}).then((response)=>{
response.json().then((svcs: Namespace[]) => {
console.log(svcs)
setNamespaces(svcs);
});
});
}
}, []);
return namespaces;
}
export function useNWSAuthKey() {
const [key, setKey] = useState('');
useEffect(() => {
let rawSession: string | null = localStorage.getItem("session_key");
if(rawSession != null) {
let session: SessionKey = JSON.parse(rawSession);
setKey(btoa(session.accountId + ":" + session.id))
}
}, []);
return key;
}
export function useGetServicesInNamespace() {
const [services, setServices] = useState<Service[]>([]);
const [ns, setNs] = useState<Namespace | null>(null);
useEffect(() => {
console.log(ns !== null ? ns.id : "null")
if(ns === null) return;
let rawSession: string | null = localStorage.getItem("session_key");
if(rawSession != null) {
let session: SessionKey = JSON.parse(rawSession);
fetch("https://api-nws.nickorlow.com/Account/" + session.accountId + "/namespaces/" + ns.id + "/services",
{
headers: {
"Authorization": btoa(session.accountId + ":" + session.id)
}
}).then((response)=>{
response.json().then((svcs: Service[]) => {
console.log(svcs)
setServices(svcs);
});
});
}
}, [ns]);
return {setNs, services, ns};
}
export function useNWSAccount() { export function useNWSAccount() {
const [accountInfo, setAccountInfo] = useState<Account>(); const [accountInfo, setAccountInfo] = useState<Account>();
@ -87,4 +153,4 @@ export function useNWSAccount() {
}, []); }, []);
return accountInfo; return accountInfo;
} }

View file

@ -3,6 +3,9 @@ export type UptimeRecord = {
url: string, url: string,
uptimeMonth: number, uptimeMonth: number,
uptimeAllTime: number, uptimeAllTime: number,
uptimeYtd: number,
averageResponseTime: number,
monitorStart: string,
isUp: boolean, isUp: boolean,
undergoingMaintenance: boolean undergoingMaintenance: boolean
}; };
@ -10,6 +13,7 @@ export type UptimeRecord = {
export type UptimeResponse = { export type UptimeResponse = {
datacenters: UptimeRecord[], datacenters: UptimeRecord[],
services: UptimeRecord[], services: UptimeRecord[],
competitors: UptimeRecord[],
lastUpdated: string lastUpdated: string
}; };
@ -64,4 +68,8 @@ export type SessionKey = {
ip: string ip: string
}; };
export type Namespace = {
id: string,
accountId: string,
name: string
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB