Merge branch 'done' of https://github.com/nickorlow/nws-site into done
This commit is contained in:
commit
ddb20ee17c
|
@ -2,10 +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/IncidentCard.tsx" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/src/components/CreateCruisePage.tsx" afterDir="false" />
|
||||||
<change afterPath="$PROJECT_DIR$/src/components/UptimeCard.tsx" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/src/components/DashboardPage.tsx" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/components/Login.css" 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$/src/App.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/App.tsx" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/App.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/App.css" 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/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" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
@ -15,6 +26,8 @@
|
||||||
<component name="FileTemplateManagerImpl">
|
<component name="FileTemplateManagerImpl">
|
||||||
<option name="RECENT_TEMPLATES">
|
<option name="RECENT_TEMPLATES">
|
||||||
<list>
|
<list>
|
||||||
|
<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" />
|
||||||
</list>
|
</list>
|
||||||
|
@ -32,21 +45,19 @@
|
||||||
<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">
|
<component name="PropertiesComponent"><![CDATA[{
|
||||||
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
"keyToString": {
|
||||||
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
"WebServerToolWindowFactoryState": "false",
|
||||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
"last_opened_file_path": "/home/nickorlow/programmming/personal/nws-site/src/components",
|
||||||
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
"list.type.of.created.stylesheet": "CSS",
|
||||||
<property name="node.js.detected.package.eslint" value="true" />
|
"nodejs_package_manager_path": "npm",
|
||||||
<property name="node.js.path.for.package.eslint" value="project" />
|
"ts.external.directory.path": "/home/nickorlow/programmming/personal/nws-site/node_modules/typescript/lib",
|
||||||
<property name="node.js.selected.package.eslint" value="(autodetect)" />
|
"vue.rearranger.settings.migration": "true"
|
||||||
<property name="nodejs_interpreter_path" value="/usr/local/bin/node" />
|
}
|
||||||
<property name="nodejs_package_manager_path" value="npm" />
|
}]]></component>
|
||||||
<property name="ts.external.directory.path" value="$PROJECT_DIR$/node_modules/typescript/lib" />
|
|
||||||
<property name="vue.rearranger.settings.migration" value="true" />
|
|
||||||
</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" />
|
||||||
</key>
|
</key>
|
||||||
</component>
|
</component>
|
||||||
|
@ -69,7 +80,8 @@
|
||||||
<workItem from="1648500425230" duration="246000" />
|
<workItem from="1648500425230" duration="246000" />
|
||||||
<workItem from="1658028513357" duration="2964000" />
|
<workItem from="1658028513357" duration="2964000" />
|
||||||
<workItem from="1666469240565" duration="7361000" />
|
<workItem from="1666469240565" duration="7361000" />
|
||||||
<workItem from="1666543043382" duration="439000" />
|
<workItem from="1666543043382" duration="3699000" />
|
||||||
|
<workItem from="1668047509596" duration="7310000" />
|
||||||
</task>
|
</task>
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
|
|
1630
package-lock.json
generated
1630
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -11,11 +11,14 @@
|
||||||
"@types/react": "^17.0.39",
|
"@types/react": "^17.0.39",
|
||||||
"@types/react-dom": "^17.0.11",
|
"@types/react-dom": "^17.0.11",
|
||||||
"bootstrap": "^5.1.3",
|
"bootstrap": "^5.1.3",
|
||||||
"react": "^17.0.2",
|
"react": "^18.2.0",
|
||||||
"react-bootstrap": "^2.4.0",
|
"react-bootstrap": "^2.4.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-markdown": "^8.0.3",
|
||||||
|
"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",
|
||||||
"typescript": "^4.5.5",
|
"typescript": "^4.5.5",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
|
|
34
src/App.css
34
src/App.css
|
@ -4,6 +4,40 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
border-radius: 10px;
|
||||||
|
border-color: #000;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
|
||||||
|
padding: 3px;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 10px;
|
||||||
|
border-width: 0px;
|
||||||
|
|
||||||
|
background-color: cornflowerblue;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 3px
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-banner {
|
||||||
|
background-color: #f08080;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 3px;
|
||||||
|
height: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
.low-severity {
|
.low-severity {
|
||||||
background-color: #98fb98
|
background-color: #98fb98
|
||||||
}
|
}
|
||||||
|
|
90
src/App.tsx
90
src/App.tsx
|
@ -1,97 +1,15 @@
|
||||||
import React, {useEffect, useState} from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import NWSLogo from './static/images/NWS_Logo.png';
|
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
import {Incident, UptimeResponse} from "./nws-api/types";
|
import StatusPage from "./components/StatusPage";
|
||||||
import {getIncidents, getUptime} from "./nws-api/calls";
|
import Footer from "./components/Footer";
|
||||||
import ReactTooltip from 'react-tooltip';
|
import { BrowserRouter, Route, Outlet, Link } from "react-router-dom";
|
||||||
import UptimeCard from "./components/UptimeCard";
|
|
||||||
import IncidentCard from "./components/IncidentCard";
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
const [uptime, setUptime] = useState<UptimeResponse>({datacenters: [], services:[], 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();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div/>
|
||||||
|
|
||||||
|
|
||||||
<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>NWS System Status</h2>
|
|
||||||
<p>Last updated at {new Date(uptime.lastUpdated).toLocaleString()}</p>
|
|
||||||
<div className={"col-md-6 col-12"}>
|
|
||||||
<h3>Service Status</h3>
|
|
||||||
{uptime.services.map((e) => {
|
|
||||||
return (
|
|
||||||
<UptimeCard uptime={e}/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div className={"col-md-6 col-12"}>
|
|
||||||
<h3>Datacenter Status</h3>
|
|
||||||
{uptime.datacenters.map((e) => {
|
|
||||||
return (
|
|
||||||
<UptimeCard uptime={e}/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{width: '75vw'}}>
|
|
||||||
<hr/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3>Service Alerts</h3>
|
|
||||||
{incidents.map((e) => {
|
|
||||||
return (
|
|
||||||
<IncidentCard incident={e}/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{incidents.length == 0 && <div className={`row text-center`} style={{width: '75vw'}}>
|
|
||||||
<h5 className={"col-12"}>No service alerts.</h5>
|
|
||||||
</div>}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<footer style={{margin: 25}}>
|
|
||||||
NWS is owned and operated by <a href={"http://nickorlow.com"}>Nicholas Orlowsky</a>.
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
src/components/Blog.css
Normal file
11
src/components/Blog.css
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
.blog-card {
|
||||||
|
transition: 1s;
|
||||||
|
width: 80%;
|
||||||
|
background-color: #eee;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card:hover {
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
41
src/components/Blogs.tsx
Normal file
41
src/components/Blogs.tsx
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
import {Blog, Incident, UptimeResponse} from "../nws-api/types";
|
||||||
|
import {getBlogs, getIncidents, getUptime} from "../nws-api/calls";
|
||||||
|
import "./Blog.css";
|
||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import strip from 'strip-markdown';
|
||||||
|
|
||||||
|
export default function Blogs(){
|
||||||
|
const [blogs, setBlogs] = useState<Blog[]>([]);
|
||||||
|
|
||||||
|
const fetchBlogs = async () => {
|
||||||
|
let resp: Blog[] = await getBlogs();
|
||||||
|
setBlogs(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchBlogs();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div>
|
||||||
|
<h1>Blogs</h1>
|
||||||
|
<div className={"d-flex justify-content-center"}>
|
||||||
|
{blogs.map((e)=>{
|
||||||
|
return(
|
||||||
|
<div className={"blog-card row"} onClick={()=>{window.location.href=`/blog?id=${e.id}`}}>
|
||||||
|
<img src={e.imageUrl} className={"col-md-4 m-0 p-0"}/>
|
||||||
|
<div className={"p-2 col-md-8"}>
|
||||||
|
<h2>{e.title}</h2>
|
||||||
|
<p>By: {e.author}</p>
|
||||||
|
<p style={{maxHeight: 100, overflow: "clip"}}><ReactMarkdown remarkPlugins={[strip]}>{e.content}</ReactMarkdown>...</p>
|
||||||
|
|
||||||
|
<p><b>Click to read more</b></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
8
src/components/CreateCruisePage.tsx
Normal file
8
src/components/CreateCruisePage.tsx
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
export default function CreateCruisePage() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Create Cruise</h1>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
35
src/components/DashboardPage.tsx
Normal file
35
src/components/DashboardPage.tsx
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import {Account, Service} from "../nws-api/types";
|
||||||
|
import {useGetAccountServices, useLoggedInRedirect, useNWSAccount} from "../nws-api/hooks";
|
||||||
|
|
||||||
|
|
||||||
|
export default function DashboardPage() {
|
||||||
|
useLoggedInRedirect();
|
||||||
|
let account: Account | undefined = useNWSAccount();
|
||||||
|
let services: Service[] = useGetAccountServices();
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div style={{minHeight: "100vh", padding: "50px"}}>
|
||||||
|
<h1>Welcome to NWS, {account?.name}!</h1>
|
||||||
|
<hr/>
|
||||||
|
<div className={"d-flex justify-content-between"}>
|
||||||
|
<h2>Your NWS Cruise™ Services</h2>
|
||||||
|
<button onClick={(e) => {window.location.href = "/cruise/new"}}>Create Cruise Service</button>
|
||||||
|
</div>
|
||||||
|
{/*<h2>Your NWS Write™ Blogs</h2>*/}
|
||||||
|
<div className={"row"}>
|
||||||
|
{services.map((e)=>{
|
||||||
|
return (
|
||||||
|
<div className={"col-4"} style={{ padding: 5}}>
|
||||||
|
<div style={{backgroundColor: "#eee", borderRadius: 20, padding: 5}}>
|
||||||
|
<h3>{e.serviceName}</h3>
|
||||||
|
<p><b>Application Id</b></p>
|
||||||
|
<p>{e.serviceId}</p>
|
||||||
|
<p><b>Deployment Key</b></p>
|
||||||
|
<a href={"#regen"}>Regenerate Deploy Key</a>
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
9
src/components/Footer.tsx
Normal file
9
src/components/Footer.tsx
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export default function Footer() {
|
||||||
|
return (
|
||||||
|
<footer style={{margin: 25}}>
|
||||||
|
NWS is owned and operated by <a href={"http://nickorlow.com"}>Nicholas Orlowsky</a>.
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
32
src/components/Login.css
Normal file
32
src/components/Login.css
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
.login-box {
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
border-color: #aaa;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
align-self: center;
|
||||||
|
justify-self: center;
|
||||||
|
|
||||||
|
width: 500px;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button {
|
||||||
|
border-radius: 10px;
|
||||||
|
border-width: 0px;
|
||||||
|
|
||||||
|
background-color: cornflowerblue;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 3px
|
||||||
|
}
|
64
src/components/LoginPage.tsx
Normal file
64
src/components/LoginPage.tsx
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import "./Login.css";
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
import {Account, ApiError, SessionKey} from "../nws-api/types";
|
||||||
|
import {useNonLoggedInRedirect} from "../nws-api/hooks";
|
||||||
|
|
||||||
|
export default function LoginPage() {
|
||||||
|
|
||||||
|
useNonLoggedInRedirect();
|
||||||
|
|
||||||
|
const [errorMessage, setErrorMessage] = useState<string>("");
|
||||||
|
const [email, setEmail] = useState<string>("");
|
||||||
|
const [password, setPassword] = useState<string>("");
|
||||||
|
|
||||||
|
function loginUser() {
|
||||||
|
if(email == "" || password == "") {
|
||||||
|
setErrorMessage("Please enter an email and password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let acc: Account = {
|
||||||
|
email: email,
|
||||||
|
password: password
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch("https://api-nws.nickorlow.com/Account/session",{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(acc)
|
||||||
|
}).then((result) => {
|
||||||
|
if(result.status == 200) {
|
||||||
|
result.json().then((o: SessionKey)=>{
|
||||||
|
localStorage.setItem("session_key", JSON.stringify(o));
|
||||||
|
window.location.href = '/dashboard';
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
setErrorMessage("Server Error (This is NWS' fault)");
|
||||||
|
}
|
||||||
|
}).catch((e) =>{
|
||||||
|
setErrorMessage("Server Error (This is NWS' fault)");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div style={{minHeight: "100vh", display: "grid", width: "100%"}}>
|
||||||
|
<div className={"login-box"}>
|
||||||
|
<h3>Login to NWS Dashboard</h3>
|
||||||
|
{ errorMessage != "" &&
|
||||||
|
<div className={"error-banner"}>
|
||||||
|
<p style={{color: "black"}}>{errorMessage}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<p className={"login-label"}>E-Mail Address</p>
|
||||||
|
<input onChange={(e)=>{setEmail(e.target.value)}} className={"login-input"}/>
|
||||||
|
<p className={"login-label"}>Password</p>
|
||||||
|
<input onChange={(e)=>{setPassword(e.target.value)}}className={"login-input"} type={"password"}/>
|
||||||
|
<button className={"login-button"} onClick={loginUser}>Login</button>
|
||||||
|
<p>No account? <a href={"/register"}>Register Here!</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
10
src/components/NotFoundPage.tsx
Normal file
10
src/components/NotFoundPage.tsx
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export default function NotFoundPage() {
|
||||||
|
return(
|
||||||
|
<div style={{width: "100vw", height: "100vh", display: "flex", justifyContent: "center", alignContent: "center", alignItems: "center"}}>
|
||||||
|
<div>
|
||||||
|
<h1>Not Found :(</h1>
|
||||||
|
<a href={"/"}>Home</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
32
src/components/RegisterPage.css
Normal file
32
src/components/RegisterPage.css
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
.reg-box {
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
border-color: #aaa;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
align-self: center;
|
||||||
|
justify-self: center;
|
||||||
|
|
||||||
|
width: 500px;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reg-label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reg-button {
|
||||||
|
border-radius: 10px;
|
||||||
|
border-width: 0px;
|
||||||
|
|
||||||
|
background-color: cornflowerblue;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 3px
|
||||||
|
}
|
105
src/components/RegisterPage.tsx
Normal file
105
src/components/RegisterPage.tsx
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
import "./RegisterPage.css";
|
||||||
|
import {useState} from "react";
|
||||||
|
import {Account, ApiError} from "../nws-api/types";
|
||||||
|
import {useNonLoggedInRedirect} from "../nws-api/hooks";
|
||||||
|
|
||||||
|
export default function RegisterPage() {
|
||||||
|
|
||||||
|
const [errorMessage, setErrorMessage] = useState<String>("");
|
||||||
|
const [name, setName] = useState<string>("");
|
||||||
|
const [email, setEmail] = useState<string>("");
|
||||||
|
const [password, setPassword] = useState<string>("");
|
||||||
|
const [cpassword, setCpassword] = useState<string>("");
|
||||||
|
const [inviteCode, setInviteCode] = useState<string>("");
|
||||||
|
const [didRegister, setDidRegister] = useState<Boolean>(false);
|
||||||
|
|
||||||
|
async function createAccount() {
|
||||||
|
|
||||||
|
if(name == "" || email == "" || password == "" || cpassword == "" || inviteCode == "") {
|
||||||
|
setErrorMessage("You must fill out all information before registering.")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cpassword != password) {
|
||||||
|
setErrorMessage("Passwords don't match!")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!email
|
||||||
|
.toLowerCase()
|
||||||
|
.match(
|
||||||
|
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||||
|
)) {
|
||||||
|
setErrorMessage("You have entered an invalid E-Mail address.")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newAcc: Account = {
|
||||||
|
email: email,
|
||||||
|
name: name,
|
||||||
|
password: password
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch("https://api-nws.nickorlow.com/Account",{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Invite-Code': inviteCode
|
||||||
|
},
|
||||||
|
body: JSON.stringify(newAcc)
|
||||||
|
}).then((result) => {
|
||||||
|
if(result.status == 200) {
|
||||||
|
setDidRegister(true);
|
||||||
|
} else {
|
||||||
|
result.json().then((o: ApiError) => {
|
||||||
|
setErrorMessage(o.ErrorMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch((e) =>{
|
||||||
|
setErrorMessage("Server Error (This is NWS' fault)")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
useNonLoggedInRedirect();
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div style={{minHeight: "100vh", display: "grid", width: "100%"}}>
|
||||||
|
<div className={"reg-box"} style={{display: didRegister ? "none" : "flex"}}>
|
||||||
|
<h3>Create an NWS Account</h3>
|
||||||
|
|
||||||
|
{ errorMessage != "" &&
|
||||||
|
<div className={"error-banner"}>
|
||||||
|
<p style={{color: "black"}}>{errorMessage}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<p className={"reg-label"}>Name</p>
|
||||||
|
<input onChange={(e)=>setName(e.target.value)} className={"reg-input"}/>
|
||||||
|
|
||||||
|
<p className={"reg-label"}>E-Mail Address</p>
|
||||||
|
<input onChange={(e)=>setEmail(e.target.value)} className={"reg-input"}/>
|
||||||
|
|
||||||
|
<p className={"reg-label"}>Password</p>
|
||||||
|
<input onChange={(e)=>setPassword(e.target.value)} className={"reg-input"} type={"password"}/>
|
||||||
|
|
||||||
|
<p className={"reg-label"}>Confirm</p>
|
||||||
|
<input onChange={(e)=>setCpassword(e.target.value)} className={"reg-input"} type={"password"}/>
|
||||||
|
|
||||||
|
<div style={{width: "100%", marginTop: 10, marginBottom: 10, padding: 10, backgroundColor: "#eee", borderRadius: 10}}>
|
||||||
|
<p className={"reg-label"}>Invite Code</p>
|
||||||
|
<input onChange={(e)=>setInviteCode(e.target.value)} style={{width: "100%"}}/>
|
||||||
|
<small>Currently, NWS is invite only. Email me to get an invite code.</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button onClick={createAccount} className={"reg-button"}>Create Account</button>
|
||||||
|
<p>Already have an account? <a href={"/login"}>Login Here!</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={"reg-box"} style={{display: didRegister ? "flex" : "none"}}>
|
||||||
|
<h3>Verify your E-Mail address.</h3>
|
||||||
|
|
||||||
|
<p>Please verify your E-Mail by clicking the link we sent to you at: <b>{email}</b></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
93
src/components/StatusPage.tsx
Normal file
93
src/components/StatusPage.tsx
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
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 StatusPage() {
|
||||||
|
|
||||||
|
const [uptime, setUptime] = useState<UptimeResponse>({datacenters: [], services: [], 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>NWS System Status</h2>
|
||||||
|
<p>Last updated at {new Date(uptime.lastUpdated).toLocaleString()}</p>
|
||||||
|
<div className={"col-md-6 col-12"}>
|
||||||
|
<h3>Service Status</h3>
|
||||||
|
{uptime.services.map((e) => {
|
||||||
|
return (
|
||||||
|
<UptimeCard uptime={e}/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className={"col-md-6 col-12"}>
|
||||||
|
<h3>Datacenter Status</h3>
|
||||||
|
{uptime.datacenters.map((e) => {
|
||||||
|
return (
|
||||||
|
<UptimeCard uptime={e}/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{width: '75vw'}}>
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>Service Alerts</h3>
|
||||||
|
{incidents.map((e) => {
|
||||||
|
return (
|
||||||
|
<IncidentCard incident={e}/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{incidents.length == 0 &&
|
||||||
|
<div className={`row text-center`} style={{width: '75vw'}}>
|
||||||
|
<h5 className={"col-12"}>No service alerts.</h5>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
32
src/components/VerifyPage.css
Normal file
32
src/components/VerifyPage.css
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
.verify-box {
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
border-color: #aaa;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
align-self: center;
|
||||||
|
justify-self: center;
|
||||||
|
|
||||||
|
width: 500px;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verify-label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verify-button {
|
||||||
|
border-radius: 10px;
|
||||||
|
border-width: 0px;
|
||||||
|
|
||||||
|
background-color: cornflowerblue;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 3px
|
||||||
|
}
|
67
src/components/VerifyPage.tsx
Normal file
67
src/components/VerifyPage.tsx
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import "./RegisterPage.css";
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
import {Account, SessionKey} from "../nws-api/types";
|
||||||
|
import {useSearchParams} from "react-router-dom";
|
||||||
|
import {Session} from "inspector";
|
||||||
|
|
||||||
|
export default function VerifyPage() {
|
||||||
|
const [pageState, setPageState] = useState<string>("");
|
||||||
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
let verificationKey: string | null = searchParams.get("key");
|
||||||
|
|
||||||
|
if(verificationKey == null) {
|
||||||
|
setPageState("invalid_code");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
fetch("https://api-nws.nickorlow.com/Account/verify?verificationKey=" + verificationKey, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.status == 200) {
|
||||||
|
result.json().then((o: SessionKey)=>{
|
||||||
|
localStorage.setItem("session_key", JSON.stringify(o));
|
||||||
|
window.location.href = '/dashboard';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status == 500) {
|
||||||
|
setPageState("server_error");
|
||||||
|
} else {
|
||||||
|
result.json().then((o) => {
|
||||||
|
if (o.ErrorMessage == "Invalid verification key.") {
|
||||||
|
setPageState("invalid_code");
|
||||||
|
} else {
|
||||||
|
setPageState("expired_code");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch((e) => {
|
||||||
|
setPageState("invalid_code");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div style={{minHeight: "100vh", display: "grid", width: "100%"}}>
|
||||||
|
<div className={"reg-box"} style={{display: pageState == "invalid_code" ? "flex" : "none"}}>
|
||||||
|
<h3>Uh Oh!</h3>
|
||||||
|
|
||||||
|
<p>Looks like the verification code you provided didn't work!</p>
|
||||||
|
|
||||||
|
<p className={"mt-2"}>Try to click on the link in the E-Mail sent to you instead of copying it.</p>
|
||||||
|
</div>
|
||||||
|
<div className={"reg-box"} style={{display: pageState == "expired_code" ? "flex" : "none"}}>
|
||||||
|
<h3>Expired Link</h3>
|
||||||
|
|
||||||
|
<p>It looks like the link you used to verify your account has expired.</p>
|
||||||
|
|
||||||
|
<p className={"mt-2"}>We've sent a new link to your email that is valid for 30 minutes.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
|
@ -11,3 +11,16 @@ code {
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||||
monospace;
|
monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-lnk {
|
||||||
|
font-weight: bold;
|
||||||
|
color: black;
|
||||||
|
text-decoration: none;
|
||||||
|
padding-left: 30px;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
transition: .5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-lnk:hover {
|
||||||
|
color: #F7BA00;
|
||||||
|
}
|
||||||
|
|
155
src/index.tsx
155
src/index.tsx
|
@ -1,14 +1,149 @@
|
||||||
import React from 'react';
|
import * as React from "react";
|
||||||
import ReactDOM from 'react-dom';
|
import * as ReactDOM from "react-dom";
|
||||||
import './index.css';
|
import {
|
||||||
import App from './App';
|
createBrowserRouter, NavLink,
|
||||||
import reportWebVitals from './reportWebVitals';
|
RouterProvider,
|
||||||
|
} from "react-router-dom";
|
||||||
|
import StatusPage from "./components/StatusPage";
|
||||||
|
import reportWebVitals from "./reportWebVitals";
|
||||||
|
import "./index.css";
|
||||||
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
|
import UptimeCard from "./components/UptimeCard";
|
||||||
|
import Footer from "./components/Footer";
|
||||||
|
import {Nav, Navbar, NavbarBrand, NavDropdown} from "react-bootstrap";
|
||||||
|
import NWSLogo from "./static/images/NWS_Logo.png";
|
||||||
|
import Blogs from "./components/Blogs";
|
||||||
|
import NotFoundPage from "./components/NotFoundPage";
|
||||||
|
import LoginPage from "./components/LoginPage";
|
||||||
|
import RegisterPage from "./components/RegisterPage";
|
||||||
|
import VerifyPage from "./components/VerifyPage";
|
||||||
|
import DashboardPage from "./components/DashboardPage";
|
||||||
|
import CreateCruisePage from "./components/CreateCruisePage";
|
||||||
|
|
||||||
ReactDOM.render(
|
function Layout (props: {children: any}) {
|
||||||
<React.StrictMode>
|
return (
|
||||||
<App />
|
<div>
|
||||||
</React.StrictMode>,
|
<Navbar sticky={"top"} style={{backgroundColor: "#eee", paddingLeft: 100, paddingRight: 100}} className={"row"}>
|
||||||
document.getElementById('root')
|
<div className={"col-10"}>
|
||||||
|
<NavbarBrand>
|
||||||
|
<img src={NWSLogo} alt="nws-logo" style={{width: 120}}/>
|
||||||
|
</NavbarBrand>
|
||||||
|
<NavLink className={"nav-lnk"} to={"/"}>
|
||||||
|
Home
|
||||||
|
</NavLink>
|
||||||
|
<NavLink className={"nav-lnk"} to={"/status"}>
|
||||||
|
Status
|
||||||
|
</NavLink>
|
||||||
|
</div>
|
||||||
|
{/*<NavLink className={"nav-lnk"} to={"/blogs"}>*/}
|
||||||
|
{/* Blog*/}
|
||||||
|
{/*</NavLink>*/}
|
||||||
|
<div className={"col-2"}>
|
||||||
|
{ localStorage.getItem("session_key") === null &&
|
||||||
|
(
|
||||||
|
<NavLink className={"nav-lnk"} to={"/login"}>
|
||||||
|
Login
|
||||||
|
</NavLink>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{ localStorage.getItem("session_key") === null ||
|
||||||
|
(
|
||||||
|
<NavDropdown title={"Account"} className={"nav-lnk"}>
|
||||||
|
<NavLink className={"nav-lnk"} to={"/dashboard"}>
|
||||||
|
Dashboard
|
||||||
|
</NavLink>
|
||||||
|
<hr/>
|
||||||
|
<NavLink className={"nav-lnk"} to={"/login"} onClick={()=>{localStorage.removeItem("session_key")}}>
|
||||||
|
Logout
|
||||||
|
</NavLink>
|
||||||
|
</NavDropdown>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</Navbar>
|
||||||
|
{props.children}
|
||||||
|
<Footer/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const router = createBrowserRouter([
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
element:
|
||||||
|
<Layout>
|
||||||
|
<StatusPage/>
|
||||||
|
</Layout>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "status",
|
||||||
|
element:
|
||||||
|
<Layout>
|
||||||
|
<StatusPage/>
|
||||||
|
</Layout>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "blog",
|
||||||
|
element:
|
||||||
|
<Layout>
|
||||||
|
<Blogs/>
|
||||||
|
</Layout>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "blogs",
|
||||||
|
element:
|
||||||
|
<Layout>
|
||||||
|
<Blogs/>
|
||||||
|
</Layout>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "login",
|
||||||
|
element:
|
||||||
|
<Layout>
|
||||||
|
<LoginPage/>
|
||||||
|
</Layout>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "verify",
|
||||||
|
element:
|
||||||
|
<Layout>
|
||||||
|
<VerifyPage/>
|
||||||
|
</Layout>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "dashboard",
|
||||||
|
element:
|
||||||
|
<Layout>
|
||||||
|
<DashboardPage/>
|
||||||
|
</Layout>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "register",
|
||||||
|
element:
|
||||||
|
<Layout>
|
||||||
|
<RegisterPage/>
|
||||||
|
</Layout>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "cruise/new",
|
||||||
|
element:
|
||||||
|
<Layout>
|
||||||
|
<CreateCruisePage/>
|
||||||
|
</Layout>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "*",
|
||||||
|
element:
|
||||||
|
<Layout>
|
||||||
|
<NotFoundPage/>
|
||||||
|
</Layout>
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
ReactDOM.createRoot(document.getElementById("root")).render(
|
||||||
|
<RouterProvider router={router} />
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
// If you want to start measuring performance in your app, pass a function
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {Incident, UptimeResponse} from "./types";
|
import {Blog, Incident, 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');
|
||||||
|
@ -17,3 +17,27 @@ export async function getIncidents(): Promise<Incident[]> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getBlogs(): Promise<Blog[]> {
|
||||||
|
let response: Response = await fetch('https://api-nws.nickorlow.com/blogs');
|
||||||
|
let blogs: Blog[] = await response.json();
|
||||||
|
return blogs;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSessionKey(accountId: string, password: string): Promise<SessionKey> {
|
||||||
|
let response: Response = await fetch('https://api-nws.nickorlow.com/Account/session',
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
'id': accountId,
|
||||||
|
'password': password
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let sessionKey: SessionKey = await response.json();
|
||||||
|
return sessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
90
src/nws-api/hooks.ts
Normal file
90
src/nws-api/hooks.ts
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
import {Account, Service, SessionKey} from "./types";
|
||||||
|
|
||||||
|
export function useNonLoggedInRedirect() {
|
||||||
|
useEffect(()=>{
|
||||||
|
let rawSession: string | null = localStorage.getItem("session_key");
|
||||||
|
|
||||||
|
if(rawSession != null) {
|
||||||
|
let session: SessionKey = JSON.parse(rawSession);
|
||||||
|
if(session.expiry < new Date()) {
|
||||||
|
localStorage.removeItem("session_key");
|
||||||
|
} else {
|
||||||
|
window.location.href = "/dashboard";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useLoggedInRedirect() {
|
||||||
|
useEffect(()=>{
|
||||||
|
let rawSession: string | null = localStorage.getItem("session_key");
|
||||||
|
|
||||||
|
if(rawSession != null) {
|
||||||
|
let session: SessionKey = JSON.parse(rawSession);
|
||||||
|
if(session.expiry > new Date()) {
|
||||||
|
window.location.href = "/login";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window.location.href = "/login";
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useGetAccountServices() {
|
||||||
|
const [services, setService] = useState<Service[]>([]);
|
||||||
|
|
||||||
|
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/services?accountId=" + session.accountId,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Authorization": btoa(session.accountId + ":" + session.id)
|
||||||
|
}
|
||||||
|
}).then((response)=>{
|
||||||
|
response.json().then((svcs: Service[]) => {
|
||||||
|
console.log(svcs)
|
||||||
|
setService(svcs);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useNWSAccount() {
|
||||||
|
const [accountInfo, setAccountInfo] = useState<Account>();
|
||||||
|
|
||||||
|
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, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": btoa(session.accountId+":"+session.id)
|
||||||
|
}
|
||||||
|
}).then((e)=>{
|
||||||
|
if(e.status == 200) {
|
||||||
|
e.json().then((o: Account) => {
|
||||||
|
setAccountInfo(o)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem("session_key");
|
||||||
|
window.location.href = "/login";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem("session_key");
|
||||||
|
window.location.href = "/login";
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return accountInfo;
|
||||||
|
}
|
|
@ -29,7 +29,39 @@ export type Incident = {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum IncidentSeverity {
|
enum IncidentSeverity {
|
||||||
LOW,
|
LOW,
|
||||||
MEDIUM,
|
MEDIUM,
|
||||||
HIGH
|
HIGH
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Below is primarily for user-facing things
|
||||||
|
|
||||||
|
export type Account = {
|
||||||
|
id?: string,
|
||||||
|
email: string,
|
||||||
|
name?: string,
|
||||||
|
password?: string,
|
||||||
|
status?: string
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Service = {
|
||||||
|
serviceId: string,
|
||||||
|
serviceName: string,
|
||||||
|
namespace: string,
|
||||||
|
containerUrl: string,
|
||||||
|
ownerId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiError = {
|
||||||
|
StatusCode: number,
|
||||||
|
ErrorMessage: string
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SessionKey = {
|
||||||
|
id: string,
|
||||||
|
expiry: Date,
|
||||||
|
accountId: string,
|
||||||
|
ip: string
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue