Add fancier server selector
This commit is contained in:
parent
924f627c58
commit
e5e614fc4b
6 changed files with 73 additions and 18 deletions
|
@ -47,7 +47,7 @@ def generate_mac(secret: str, nonce: str, user: str, password: str, admin: bool
|
|||
|
||||
@routes.get("/client/auth/servers")
|
||||
async def get_registerable_servers(_: web.Request) -> web.Response:
|
||||
return web.json_response(list(registration_secrets().keys()))
|
||||
return web.json_response({key: value["url"] for key, value in registration_secrets().items()})
|
||||
|
||||
|
||||
AuthRequestInfo = NamedTuple("AuthRequestInfo", api=HTTPAPI, secret=str, username=str, password=str)
|
||||
|
|
|
@ -410,13 +410,14 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
description: Key-value map from server name to homeserver URL
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: The homeserver URL
|
||||
example:
|
||||
- maunium.net
|
||||
- example.com
|
||||
- matrix.org
|
||||
maunium.net: https://maunium.net
|
||||
example.com: https://matrix.example.org
|
||||
401:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'/client/auth/{server}/register':
|
||||
|
|
|
@ -36,8 +36,8 @@ async function defaultDelete(type, id) {
|
|||
return await resp.json()
|
||||
}
|
||||
|
||||
async function defaultPut(type, entry, id = undefined) {
|
||||
const resp = await fetch(`${BASE_PATH}/${type}/${id || entry.id}`, {
|
||||
async function defaultPut(type, entry, id = undefined, suffix = undefined) {
|
||||
const resp = await fetch(`${BASE_PATH}/${type}/${id || entry.id}${suffix}`, {
|
||||
headers: getHeaders(),
|
||||
body: JSON.stringify(entry),
|
||||
method: "PUT",
|
||||
|
@ -221,6 +221,17 @@ export function getAvatarURL({ id, avatar_url }) {
|
|||
export const putClient = client => defaultPut("client", client)
|
||||
export const deleteClient = id => defaultDelete("client", id)
|
||||
|
||||
export const getClientAuthServers = () => defaultGet("/client/auth/servers")
|
||||
|
||||
export async function doClientAuth(server, type, username, password) {
|
||||
const resp = await fetch(`${BASE_PATH}/client/auth/${server}/${type}`, {
|
||||
headers: getHeaders(),
|
||||
body: JSON.stringify({ username, password }),
|
||||
method: "POST",
|
||||
})
|
||||
return await resp.json()
|
||||
}
|
||||
|
||||
export default {
|
||||
BASE_PATH,
|
||||
login, ping, getFeatures, remoteGetFeatures,
|
||||
|
@ -230,4 +241,5 @@ export default {
|
|||
getInstanceDatabase, queryInstanceDatabase,
|
||||
getPlugins, getPlugin, uploadPlugin, deletePlugin,
|
||||
getClients, getClient, uploadAvatar, getAvatarURL, putClient, deleteClient,
|
||||
getClientAuthServers, doClientAuth
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import React from "react"
|
||||
import Select from "react-select"
|
||||
import CreatableSelect from "react-select/creatable"
|
||||
import Switch from "./Switch"
|
||||
|
||||
export const PrefTable = ({ children, wrapperClass }) => {
|
||||
|
@ -56,10 +57,12 @@ export const PrefSwitch = ({ rowName, active, origActive, fullWidth = false, ...
|
|||
</PrefRow>
|
||||
)
|
||||
|
||||
export const PrefSelect = ({ rowName, value, origValue, fullWidth = false, ...args }) => (
|
||||
export const PrefSelect = ({ rowName, value, origValue, fullWidth = false, creatable = false, ...args }) => (
|
||||
<PrefRow name={rowName} fullWidth={fullWidth} labelFor={rowName}
|
||||
changed={origValue !== undefined && value.id !== origValue}>
|
||||
<Select className="select" {...args} id={rowName} value={value}/>
|
||||
{creatable
|
||||
? <CreatableSelect className="select" {...args} id={rowName} value={value}/>
|
||||
: <Select className="select" {...args} id={rowName} value={value}/>}
|
||||
</PrefRow>
|
||||
)
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import React from "react"
|
|||
import { NavLink, withRouter } from "react-router-dom"
|
||||
import { ReactComponent as ChevronRight } from "../../res/chevron-right.svg"
|
||||
import { ReactComponent as UploadButton } from "../../res/upload.svg"
|
||||
import { PrefTable, PrefSwitch, PrefInput } from "../../components/PreferenceTable"
|
||||
import { PrefTable, PrefSwitch, PrefInput, PrefSelect } from "../../components/PreferenceTable"
|
||||
import Spinner from "../../components/Spinner"
|
||||
import api from "../../api"
|
||||
import BaseMainView from "./BaseMainView"
|
||||
|
@ -84,6 +84,36 @@ class Client extends BaseMainView {
|
|||
return client
|
||||
}
|
||||
|
||||
get selectedHomeserver() {
|
||||
return this.state.homeserver
|
||||
? this.homeserverEntry([this.props.ctx.homeserversByURL[this.state.homeserver],
|
||||
this.state.homeserver])
|
||||
: {}
|
||||
}
|
||||
|
||||
homeserverEntry = ([serverName, serverURL]) => serverURL && {
|
||||
id: serverURL,
|
||||
value: serverURL,
|
||||
label: serverName || serverURL,
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
super.componentWillReceiveProps(nextProps)
|
||||
this.updateHomeserverOptions()
|
||||
}
|
||||
|
||||
updateHomeserverOptions() {
|
||||
this.homeserverOptions = Object.entries(this.props.ctx.homeserversByName).map(this.homeserverEntry)
|
||||
}
|
||||
|
||||
isValidHomeserver(value) {
|
||||
try {
|
||||
return Boolean(new URL(value))
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
avatarUpload = async event => {
|
||||
const file = event.target.files[0]
|
||||
this.setState({
|
||||
|
@ -165,9 +195,10 @@ class Client extends BaseMainView {
|
|||
name={this.isNew ? "id" : ""} className="id"
|
||||
value={this.state.id} origValue={this.props.entry.id}
|
||||
placeholder="@fancybot:example.com" onChange={this.inputChange}/>
|
||||
<PrefInput rowName="Homeserver" type="text" name="homeserver"
|
||||
value={this.state.homeserver} origValue={this.props.entry.homeserver}
|
||||
placeholder="https://example.com" onChange={this.inputChange}/>
|
||||
<PrefSelect rowName="Homeserver" options={this.homeserverOptions} isSearchable={true}
|
||||
value={this.selectedHomeserver} origValue={this.props.entry.homeserver}
|
||||
onChange={({ id }) => this.setState({ homeserver: id })}
|
||||
creatable={true} isValidNewOption={this.isValidHomeserver}/>
|
||||
<PrefInput rowName="Access token" type="text" name="access_token"
|
||||
value={this.state.access_token} origValue={this.props.entry.access_token}
|
||||
placeholder="MDAxYWxvY2F0aW9uIG1hdHJpeC5sb2NhbAowMDEzaWRlbnRpZmllc"
|
||||
|
|
|
@ -31,6 +31,8 @@ class Dashboard extends Component {
|
|||
instances: {},
|
||||
clients: {},
|
||||
plugins: {},
|
||||
homeserversByName: {},
|
||||
homeserversByURL: {},
|
||||
sidebarOpen: false,
|
||||
modalOpen: false,
|
||||
logFocus: null,
|
||||
|
@ -50,8 +52,8 @@ class Dashboard extends Component {
|
|||
}
|
||||
|
||||
async componentWillMount() {
|
||||
const [instanceList, clientList, pluginList] = await Promise.all([
|
||||
api.getInstances(), api.getClients(), api.getPlugins(),
|
||||
const [instanceList, clientList, pluginList, homeservers] = await Promise.all([
|
||||
api.getInstances(), api.getClients(), api.getPlugins(), api.getClientAuthServers(),
|
||||
api.updateDebugOpenFileEnabled()])
|
||||
const instances = {}
|
||||
if (api.getFeatures().instance) {
|
||||
|
@ -71,7 +73,13 @@ class Dashboard extends Component {
|
|||
plugins[plugin.id] = plugin
|
||||
}
|
||||
}
|
||||
this.setState({ instances, clients, plugins })
|
||||
const homeserversByName = homeservers
|
||||
const homeserversByURL = {}
|
||||
for (const [key, value] of Object.entries(homeservers)) {
|
||||
homeserversByURL[value] = key
|
||||
}
|
||||
console.log(homeserversByName, homeserversByURL)
|
||||
this.setState({ instances, clients, plugins, homeserversByName, homeserversByURL })
|
||||
|
||||
await this.enableLogs()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue