diff --git a/net/server/internal/api_status.go b/net/server/internal/api_status.go index fa014b22..0ac3a545 100644 --- a/net/server/internal/api_status.go +++ b/net/server/internal/api_status.go @@ -90,11 +90,11 @@ func Status(w http.ResponseWriter, r *http.Request) { return } case <-ticker.C: + conn.SetWriteDeadline(time.Now().Add(15 * time.Second)) if err := conn.WriteMessage(websocket.PingMessage, nil); err != nil { ErrMsg(err) return } - conn.SetReadDeadline(time.Now().Add(15 * time.Second)) case <-wsExit: LogMsg("exiting server") wsExit<-true diff --git a/net/web/package.json b/net/web/package.json index 69af292d..7ed800a8 100644 --- a/net/web/package.json +++ b/net/web/package.json @@ -10,6 +10,7 @@ "base-64": "^1.0.0", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-router-dom": "^6.2.2", "react-scripts": "5.0.0", "web-vitals": "^2.1.0" }, diff --git a/net/web/src/App.js b/net/web/src/App.js index 75727b45..a00974c7 100644 --- a/net/web/src/App.js +++ b/net/web/src/App.js @@ -1,256 +1,19 @@ -import React, { useState, useEffect, useRef } from 'react' +import React, { useContext, useState, useEffect, useRef } from 'react' import login from './login.png'; import { Input, Button } from 'antd'; import { UserOutlined, LockOutlined } from '@ant-design/icons'; import 'antd/dist/antd.css'; - -var base64 = require('base-64'); - -const FETCH_TIMEOUT = 15000; - -function checkResponse(response) { - if(response.status >= 400 && response.status < 600) { - throw new Error(response.url + " failed"); - } -} - -async function fetchWithTimeout(url, options) { - return Promise.race([ - fetch(url, options).catch(err => { throw new Error(url + ' failed'); }), - new Promise((_, reject) => setTimeout(() => reject(new Error(url + ' timeout')), FETCH_TIMEOUT)) - ]); -} - -async function getAvailable() { - let available = await fetchWithTimeout("/account/available", { method: 'GET', timeout: FETCH_TIMEOUT }) - checkResponse(available) - return await available.json() -} - -async function getUsername(name: string) { - let available = await fetchWithTimeout('/account/username?name=' + encodeURIComponent(name), { method: 'GET', timeout: FETCH_TIMEOUT }) - checkResponse(available) - return await available.json() -} - -async function setLogin(username: string, password: string) { - let headers = new Headers() - headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password)); - let app = { Name: "indicom", Description: "decentralized communication" } - let login = await fetchWithTimeout('/account/apps', { method: 'POST', timeout: FETCH_TIMEOUT, body: JSON.stringify(app), headers: headers }) - checkResponse(login) - return await login.json() -} - -async function createAccount(username: string, password: string) { - let headers = new Headers() - headers.append('Credentials', 'Basic ' + base64.encode(username + ":" + password)); - let profile = await fetchWithTimeout("/account/profile", { method: 'POST', timeout: FETCH_TIMEOUT, headers: headers }) - checkResponse(profile); - return await profile.json() -} +import { BrowserRouter as Router, Routes, Route, useHistory } from "react-router-dom"; +import { AppContextProvider } from './context/AppContext'; +import { Root } from './components/Root'; function App() { - const [available, setAvailable] = useState(false) - const [username, setUsername] = useState('') - const [password, setPassword] = useState('') - const [confirmed, setConfirmed] = useState('') - const [mode, setMode] = useState('') - const [creatable, setCreatable] = useState(false) - const [conflict, setConflict] = useState('') - const [token, setToken] = useState('') - const debounce = useRef(null) - const ws = useRef(null) - useEffect(() => { - let access = localStorage.getItem("access") - console.log("ACCESS", access) - - if (access == null) { - setMode('login') - getAvailable().then(a => { - setAvailable(a > 0) - }).catch(err => { - console.log(err) - }) - } - else { - setMode('logout') - connectStatus(access) - } - - }, []) - - const usernameSet = (name) => { - setCreatable(false) - setUsername(name) - clearTimeout(debounce.current) - debounce.current = setTimeout(async () => { - let valid = await getUsername(name) - setCreatable(valid) - if (!valid) { - setConflict('not available') - } else { - setConflict('') - } - setCreatable(await getUsername(name)) - }, 500) - } - - const connectStatus = (access: string) => { - ws.current = new WebSocket("wss://" + window.location.host + "/status"); - ws.current.onmessage = (ev) => { - console.log(ev) - } - ws.current.onclose = () => { - console.log('ws close') - setTimeout(() => { - if (ws.current != null) { - ws.current.onmessage = () => {} - ws.current.onclose = () => {} - ws.current.onopen = () => {} - ws.current.onerror = () => {} - connectStatus(access) - } - }, 2000) - } - ws.current.onopen = () => { - ws.current.send(JSON.stringify({ AppToken: access })) - } - ws.current.error = () => { - console.log('ws error') - } - localStorage.setItem("access", access) - } - - const Logout = () => { - if (mode === 'logout') { - return - } - return <> - } - - const Link = () => { - if (mode === 'create') { - return - } - if (mode === 'login') { - return - } - return <> - } - - const canLogin = () => { - return username !== '' && password !== '' - } - - const canCreate = () => { - return username !== '' && password !== '' && confirmed === password && creatable - } - - const onLogin = async () => { - try { - let access = await setLogin(username, password) - connectStatus(access) - setMode('logout') - console.log(access) - } - catch(err) { - window.alert("failed to sign into account") - } - } - - const onCreate = async () => { - try { - let profile = await createAccount(username, password) - setMode('created') - try { - let access = await setLogin(username, password) - connectStatus(access) - setMode('logout') - console.log(access) - } - catch(err) { - window.alert("failed to sign into account") - } - } - catch(err) { - window.alert("failed to create account") - } - } - - const onLogout = () => { - ws.current.onclose = () => {} - ws.current.close(1000, "bye") - ws.current = null - localStorage.removeItem("access") - setMode('login') - } - - if (mode === 'login') { - return ( -
- -
-
-
indicom
-
- Communication for the Decentralized Web -
- usernameSet(e.target.value)} value={username} placeholder="username" prefix={} style={{ marginTop: '16px' }} /> - setPassword(e.target.value)} value={password} placeholder="password" prefix={} style={{ marginTop: '16px' }} /> - -
- -
-
- ) - } - if (mode === 'create') { - return ( -
- -
-
-
indicom
-
- Communication for the Decentralized Web -
- usernameSet(e.target.value)} value={username} placeholder="username" prefix={} style={{ marginTop: '16px' }} /> - setPassword(e.target.value)} value={password} placeholder="password" prefix={} style={{ marginTop: '16px' }} /> - setConfirmed(e.target.value)} value={confirmed} placeholder="confirm password" prefix={} style={{ marginTop: '16px' }} /> - -
- -
-
- ) - } - if (mode === 'logout') { - return ( -
- -
-
-
indicom
-
- Communication for the Decentralized Web -
- -
- -
-
- ) - } - else { - return ( -
- -
- ) - } - return <> + return ( + + + + ); } export default App; diff --git a/net/web/src/components/Root.js b/net/web/src/components/Root.js new file mode 100644 index 00000000..6fb84487 --- /dev/null +++ b/net/web/src/components/Root.js @@ -0,0 +1,44 @@ +import React, { useContext, useState, useEffect, useRef } from 'react' +import { BrowserRouter as Router, Routes, Route, useNavigate } from "react-router-dom"; +import { AppContext } from '../context/AppContext'; + +export function Root() { + + return ( + + + } /> + } /> + } /> + + + ) +} + +function About() { + return (
ABOUT
) +} + +function Topic() { + return (
TOPIC
) +} + +function Empty() { + + const appContext = useContext(AppContext); + const navigate = useNavigate(); + + useEffect(() => { + console.log(appContext) + if (appContext.state) { + if (appContext.state.appToken) { + navigate("/topic") + } else { + navigate("/about") + } + } + }) + + return (
EMPTY
) +} + diff --git a/net/web/src/context/AppContext.js b/net/web/src/context/AppContext.js new file mode 100644 index 00000000..3ce23469 --- /dev/null +++ b/net/web/src/context/AppContext.js @@ -0,0 +1,14 @@ +import { createContext } from 'react'; +import useAppContext from './useAppContext.hook'; + +export const AppContext = createContext({}); + +export function AppContextProvider({ children }) { + const { state, actions } = useAppContext(); + return ( + + {children} + + ); +} + diff --git a/net/web/src/context/useAppContext.hook.js b/net/web/src/context/useAppContext.hook.js new file mode 100644 index 00000000..ff86d15d --- /dev/null +++ b/net/web/src/context/useAppContext.hook.js @@ -0,0 +1,39 @@ +import { useEffect, useState } from 'react'; + +export default function useAppContext() { + const [state, setState] = useState(null); + + const actions = { + setListener: setListener, + clearListener: clearListener, + login: login, + logout: logout, + } + + useEffect(() => { + const token = localStorage.getItem('app_token'); + if (token) { + setState({ appToken: token }) + } else { + setState({ appToken: null }) + } + }, []); + return { state, actions }; +} + +function setListener(name: string, callback: (objectId: string) => void) { + return +} + +function clearListener(callback: (objectId: string) => void) { + return +} + +async function login(username: string, password: string) { + return +} + +async function logout() { + return +} + diff --git a/net/web/yarn.lock b/net/web/yarn.lock index 568090a5..31440481 100644 --- a/net/web/yarn.lock +++ b/net/web/yarn.lock @@ -1065,6 +1065,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.7.6": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.7.tgz#a5f3328dc41ff39d803f311cfe17703418cf9825" + integrity sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.16.7", "@babel/template@^7.3.3": version "7.16.7" resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz" @@ -4628,6 +4635,13 @@ he@^1.2.0: resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +history@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b" + integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ== + dependencies: + "@babel/runtime" "^7.7.6" + hoopy@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz" @@ -7572,6 +7586,21 @@ react-refresh@^0.11.0: resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== +react-router-dom@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.2.2.tgz#f1a2c88365593c76b9612ae80154a13fcb72e442" + integrity sha512-AtYEsAST7bDD4dLSQHDnk/qxWLJdad5t1HFa1qJyUrCeGgEuCSw0VB/27ARbF9Fi/W5598ujvJOm3ujUCVzuYQ== + dependencies: + history "^5.2.0" + react-router "6.2.2" + +react-router@6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.2.2.tgz#495e683a0c04461eeb3d705fe445d6cf42f0c249" + integrity sha512-/MbxyLzd7Q7amp4gDOGaYvXwhEojkJD5BtExkuKmj39VEE0m3l/zipf6h2WIB2jyAO0lI6NGETh4RDcktRm4AQ== + dependencies: + history "^5.2.0" + react-scripts@5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.0.tgz"