updating web app output path

This commit is contained in:
Roland Osborne 2022-06-08 12:52:06 -07:00
parent 74244b920d
commit f93b51c862
15 changed files with 134 additions and 50 deletions

View File

@ -12,8 +12,15 @@ import (
func AddAccount(w http.ResponseWriter, r *http.Request) { func AddAccount(w http.ResponseWriter, r *http.Request) {
var token *store.AccountToken var token *store.AccountToken
var res error
if r.Header.Get("Authorization") == "" { if r.FormValue("token") != "" {
token, _, res = AccessToken(r)
if res != nil || token.TokenType != APP_TOKENCREATE {
ErrResponse(w, http.StatusUnauthorized, res)
return
}
} else {
if available, err := getAvailableAccounts(); err != nil { if available, err := getAvailableAccounts(); err != nil {
ErrResponse(w, http.StatusInternalServerError, err) ErrResponse(w, http.StatusInternalServerError, err)
return return
@ -21,13 +28,6 @@ func AddAccount(w http.ResponseWriter, r *http.Request) {
ErrResponse(w, http.StatusForbidden, errors.New("no open accounts available")) ErrResponse(w, http.StatusForbidden, errors.New("no open accounts available"))
return return
} }
} else {
var err error
token, err = BearerAccountToken(r);
if err != nil || token.TokenType != APP_TOKENCREATE {
ErrResponse(w, http.StatusUnauthorized, err)
return
}
} }
username, password, ret := BasicCredentials(r); username, password, ret := BasicCredentials(r);

View File

@ -11,21 +11,19 @@ type accountUsername struct {
} }
func GetAccountUsername(w http.ResponseWriter, r *http.Request) { func GetAccountUsername(w http.ResponseWriter, r *http.Request) {
var token *store.AccountToken
if r.Header.Get("Authorization") == "" { if r.FormValue("token") != "" {
token, _, res := AccessToken(r)
if res != nil || token.TokenType != APP_TOKENCREATE {
ErrResponse(w, http.StatusUnauthorized, res)
return
}
} else {
if available, err := getAvailableAccounts(); err != nil { if available, err := getAvailableAccounts(); err != nil {
ErrResponse(w, http.StatusInternalServerError, err) ErrResponse(w, http.StatusInternalServerError, err)
return return
} else if available == 0 { } else if available == 0 {
ErrResponse(w, http.StatusUnauthorized, errors.New("no open accounts available")) ErrResponse(w, http.StatusForbidden, errors.New("no open accounts available"))
return
}
} else {
var err error
token, err = BearerAccountToken(r);
if err != nil || token.TokenType != APP_TOKENCREATE {
ErrResponse(w, http.StatusUnauthorized, err)
return return
} }
} }

View File

@ -34,7 +34,7 @@ func NewRouter() *mux.Router {
Handler(handler) Handler(handler)
} }
fs := http.FileServer(http.Dir("/data/databag/net/web/build/")) fs := http.FileServer(http.Dir("/app/databag/net/web/build/"))
router.PathPrefix("/").Handler(http.StripPrefix("/", fs)) router.PathPrefix("/").Handler(http.StripPrefix("/", fs))
return router return router

View File

@ -45,7 +45,7 @@ export function AccountItem({ token, item, remove }) {
<div class="control"> <div class="control">
<Tooltip placement="topLeft" title="Account Login Link"> <Tooltip placement="topLeft" title="Account Login Link">
<ResetButton type="text" size="large" icon={<UnlockOutlined />} <ResetButton type="text" size="large" icon={<UnlockOutlined />}
onClick={() => actions.setAccessLink()}></ResetButton> loading={state.accessBusy} onClick={() => actions.setAccessLink()}></ResetButton>
</Tooltip> </Tooltip>
<Enable /> <Enable />
<Tooltip placement="topLeft" title="Delete Account"> <Tooltip placement="topLeft" title="Delete Account">
@ -53,7 +53,7 @@ export function AccountItem({ token, item, remove }) {
loading={state.removeBusy} onClick={() => actions.remove()}></DeleteButton> loading={state.removeBusy} onClick={() => actions.remove()}></DeleteButton>
</Tooltip> </Tooltip>
</div> </div>
<Modal title="Access Link" visible={state.showAccess} centered width="fitContent" <Modal title="Access Account Link" visible={state.showAccess} centered width="fitContent"
footer={[ <Button type="primary" onClick={() => actions.setShowAccess(false)}>OK</Button> ]} footer={[ <Button type="primary" onClick={() => actions.setShowAccess(false)}>OK</Button> ]}
onCancel={() => actions.setShowAccess(false)}> onCancel={() => actions.setShowAccess(false)}>
<AccessLayout> <AccessLayout>

View File

@ -8,6 +8,7 @@ export function useAccountItem(token, item, remove) {
const [state, setState] = useState({ const [state, setState] = useState({
statusBusy: false, statusBusy: false,
removeBusy: false, removeBusy: false,
accessBusy: false,
showAccess: false, showAccess: false,
}); });
@ -29,8 +30,17 @@ export function useAccountItem(token, item, remove) {
const actions = { const actions = {
setAccessLink: async () => { setAccessLink: async () => {
let access = await addAccountAccess(token, item.accountId); if (!state.accessBusy) {
updateState({ accessToken: access, showAccess: true }); updateState({ accessBusy: true });
try {
let access = await addAccountAccess(token, item.accountId);
updateState({ accessToken: access, showAccess: true });
}
catch (err) {
window.alert(err);
}
updateState({ accessBusy: false });
}
}, },
setShowAccess: (showAccess) => { setShowAccess: (showAccess) => {
updateState({ showAccess }); updateState({ showAccess });

View File

@ -1,6 +1,6 @@
import { DashboardWrapper, SettingsButton, AddButton, SettingsLayout } from './Dashboard.styled'; import { DashboardWrapper, SettingsButton, AddButton, SettingsLayout, CreateLayout } from './Dashboard.styled';
import { Tooltip, Button, Modal, Input, InputNumber, Space, List } from 'antd'; import { Tooltip, Button, Modal, Input, InputNumber, Space, List } from 'antd';
import { SettingOutlined, UserAddOutlined, LogoutOutlined, ReloadOutlined } from '@ant-design/icons'; import { SettingOutlined, CopyOutlined, UserAddOutlined, LogoutOutlined, ReloadOutlined } from '@ant-design/icons';
import { useDashboard } from './useDashboard.hook'; import { useDashboard } from './useDashboard.hook';
import { AccountItem } from './AccountItem/AccountItem'; import { AccountItem } from './AccountItem/AccountItem';
@ -8,6 +8,14 @@ export function Dashboard({ token, config, logout }) {
const { state, actions } = useDashboard(token, config); const { state, actions } = useDashboard(token, config);
const onClipboard = (value) => {
navigator.clipboard.writeText(value);
};
const createLink = () => {
return window.location.origin + '/#/create?add=' + state.createToken;
};
return ( return (
<DashboardWrapper> <DashboardWrapper>
@ -34,7 +42,8 @@ export function Dashboard({ token, config, logout }) {
</div> </div>
<div class="add"> <div class="add">
<Tooltip placement="topRight" title="Create Account Link"> <Tooltip placement="topRight" title="Create Account Link">
<AddButton type="text" size="large" icon={<UserAddOutlined />}></AddButton> <AddButton type="text" size="large" icon={<UserAddOutlined />}
loading={state.createBusy} onClick={() => actions.setCreateLink()}></AddButton>
</Tooltip> </Tooltip>
</div> </div>
</div> </div>
@ -60,13 +69,22 @@ export function Dashboard({ token, config, logout }) {
value={state.host} /> value={state.host} />
</div> </div>
<div class="storage"> <div class="storage">
<div>Account Storage (GB):&nbsp;</div> <div>Storage Limit (GB) / Account:&nbsp;</div>
<InputNumber defaultValue={8} onChange={(e) => actions.setStorage(e)} <InputNumber defaultValue={8} onChange={(e) => actions.setStorage(e)}
placeholder="0 for unrestricted" value={state.storage} /> placeholder="0 for unrestricted" value={state.storage} />
</div> </div>
</SettingsLayout> </SettingsLayout>
</Modal> </Modal>
<Modal title="Create Account Link" visible={state.showCreate} centered width="fitContent"
footer={[ <Button type="primary" onClick={() => actions.setShowCreate(false)}>OK</Button> ]}
onCancel={() => actions.setShowCreate(false)}>
<CreateLayout>
<div>{createLink()}</div>
<Button icon={<CopyOutlined />} size="small"
onClick={() => onClipboard(createLink())}
/>
</CreateLayout>
</Modal>
</DashboardWrapper> </DashboardWrapper>
); );
} }

View File

@ -84,3 +84,7 @@ export const SettingsLayout = styled(Space)`
align-items: center; align-items: center;
} }
`; `;
export const CreateLayout = styled(Space)`
white-space: nowrap;
`

View File

@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
import { setNodeConfig } from 'api/setNodeConfig'; import { setNodeConfig } from 'api/setNodeConfig';
import { getNodeAccounts } from 'api/getNodeAccounts'; import { getNodeAccounts } from 'api/getNodeAccounts';
import { removeAccount } from 'api/removeAccount'; import { removeAccount } from 'api/removeAccount';
import { addAccountCreate } from 'api/addAccountCreate';
export function useDashboard(token, config) { export function useDashboard(token, config) {
@ -12,6 +13,8 @@ export function useDashboard(token, config) {
busy: false, busy: false,
loading: false, loading: false,
accounts: [], accounts: [],
createBusy: false,
showCreate: false,
}); });
const updateState = (value) => { const updateState = (value) => {
@ -19,6 +22,22 @@ export function useDashboard(token, config) {
} }
const actions = { const actions = {
setCreateLink: async () => {
if (!state.createBusy) {
updateState({ createBusy: true });
try {
let create = await addAccountCreate(token)
updateState({ createToken: create, showCreate: true });
}
catch (err) {
window.alert(err);
}
updateState({ createBusy: false });
}
},
setShowCreate: (showCreate) => {
updateState({ showCreate });
},
removeAccount: async (accountId) => { removeAccount: async (accountId) => {
await removeAccount(token, accountId); await removeAccount(token, accountId);
actions.getAccounts(); actions.getAccounts();

View File

@ -1,6 +1,6 @@
import { useContext, useState, useEffect, useRef } from 'react'; import { useContext, useState, useEffect, useRef } from 'react';
import { AppContext } from 'context/AppContext'; import { AppContext } from 'context/AppContext';
import { useNavigate } from "react-router-dom"; import { useNavigate, useLocation } from "react-router-dom";
export function useCreate() { export function useCreate() {
const [checked, setChecked] = useState(true) const [checked, setChecked] = useState(true)
@ -10,10 +10,12 @@ export function useCreate() {
confirmed: '', confirmed: '',
conflict: '', conflict: '',
spinning: false, spinning: false,
token: null,
}); });
const navigate = useNavigate(); const navigate = useNavigate();
const app = useContext(AppContext); const app = useContext(AppContext);
const { search } = useLocation();
const debounce = useRef(null) const debounce = useRef(null)
const actions = { const actions = {
@ -41,7 +43,7 @@ export function useCreate() {
if (!state.spinning) { if (!state.spinning) {
actions.updateState({ spinning: true }) actions.updateState({ spinning: true })
try { try {
await app.actions.create(state.username, state.password) await app.actions.create(state.username, state.password, state.token)
} }
catch (err) { catch (err) {
window.alert(err); window.alert(err);
@ -64,7 +66,7 @@ export function useCreate() {
actions.updateState({ conflict: '' }) actions.updateState({ conflict: '' })
} }
else { else {
let valid = await app.actions.username(name) let valid = await app.actions.username(name, state.token)
setChecked(true) setChecked(true)
if (!valid) { if (!valid) {
actions.updateState({ conflict: 'not available' }) actions.updateState({ conflict: 'not available' })
@ -82,8 +84,12 @@ export function useCreate() {
if (app.state.access === 'user') { if (app.state.access === 'user') {
navigate('/user') navigate('/user')
} }
if (app.state.access === 'admin') { else {
navigate('/admin') let params = new URLSearchParams(search);
let token = params.get("add");
if (token) {
actions.updateState({ token });
}
} }
} }
} }

View File

@ -43,10 +43,16 @@ export function ChannelLabel({ item }) {
} }
if (item?.data?.channelDetail?.data) { if (item?.data?.channelDetail?.data) {
let data = JSON.parse(item.data.channelDetail.data); try {
if (data.subject != '' && data.subject != null) { let data = JSON.parse(item.data.channelDetail.data);
setSubject(data.subject); if (data.subject != '' && data.subject != null) {
return setSubject(data.subject);
return
}
}
catch (err) {
console.log(err);
setSubject(null);
} }
} }
setSubject(contacts.join(', ')); setSubject(contacts.join(', '));

View File

@ -0,0 +1,15 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
var base64 = require('base-64');
export async function addAccount(username, password, token) {
let access = "";
if (token) {
access = `?token=${token}`
}
let headers = new Headers()
headers.append('Credentials', 'Basic ' + base64.encode(username + ":" + password));
let profile = await fetchWithTimeout(`/account/profile${access}`, { method: 'POST', headers: headers })
checkResponse(profile);
return await profile.json()
}

View File

@ -0,0 +1,8 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function addAccountCreate(token) {
let access = await fetchWithTimeout(`/admin/accounts?token=${token}`, { method: 'POST' })
checkResponse(access);
return await access.json()
}

View File

@ -1,7 +1,11 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil'; import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function getUsername(name) { export async function getUsername(name, token) {
let available = await fetchWithTimeout('/account/username?name=' + encodeURIComponent(name), { method: 'GET' }) let access = "";
if (token) {
access = `&token=${token}`
}
let available = await fetchWithTimeout('/account/username?name=' + encodeURIComponent(name) + access, { method: 'GET' })
checkResponse(available) checkResponse(available)
return await available.json() return await available.json()
} }

View File

@ -21,12 +21,6 @@ export async function getAvailable() {
return await available.json() return await available.json()
} }
export async function getUsername(name) {
let available = await fetchWithTimeout('/account/username?name=' + encodeURIComponent(name), { method: 'GET', timeout: FETCH_TIMEOUT })
checkResponse(available)
return await available.json()
}
export async function setLogin(username, password) { export async function setLogin(username, password) {
let headers = new Headers() let headers = new Headers()
headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password)); headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));

View File

@ -1,7 +1,9 @@
import { useEffect, useState, useRef, useContext } from 'react'; import { useEffect, useState, useRef, useContext } from 'react';
import { useNavigate, useLocation, useParams } from "react-router-dom"; import { useNavigate, useLocation, useParams } from "react-router-dom";
import { getAvailable, getUsername, setLogin, createAccount } from './fetchUtil'; import { getAvailable, setLogin } from './fetchUtil';
import { setAccountAccess } from 'api/setAccountAccess'; import { setAccountAccess } from 'api/setAccountAccess';
import { addAccount } from 'api/addAccount';
import { getUsername } from 'api/getUsername';
import { AccountContext } from './AccountContext'; import { AccountContext } from './AccountContext';
import { ProfileContext } from './ProfileContext'; import { ProfileContext } from './ProfileContext';
import { ArticleContext } from './ArticleContext'; import { ArticleContext } from './ArticleContext';
@ -9,8 +11,8 @@ import { GroupContext } from './GroupContext';
import { CardContext } from './CardContext'; import { CardContext } from './CardContext';
import { ChannelContext } from './ChannelContext'; import { ChannelContext } from './ChannelContext';
async function appCreate(username, password, updateState, setWebsocket) { async function appCreate(username, password, token, updateState, setWebsocket) {
await createAccount(username, password); await addAccount(username, password, token);
let access = await setLogin(username, password) let access = await setLogin(username, password)
updateState({ token: access, access: 'user' }); updateState({ token: access, access: 'user' });
setWebsocket(access) setWebsocket(access)
@ -95,8 +97,8 @@ export function useAppContext() {
login: async (username, password) => { login: async (username, password) => {
await appLogin(username, password, updateState, setWebsocket) await appLogin(username, password, updateState, setWebsocket)
}, },
create: async (username, password) => { create: async (username, password, token) => {
await appCreate(username, password, updateState, setWebsocket) await appCreate(username, password, token, updateState, setWebsocket)
}, },
username: getUsername, username: getUsername,
available: getAvailable, available: getAvailable,