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 ( -