зеркало из https://github.com/mozilla/hubs.git
/token endpoint rendering page, wip TokenPageLayout remove Storybook context to AuthContext
This commit is contained in:
Родитель
3ea3c27468
Коммит
182c2a689a
|
@ -60,6 +60,12 @@ module.exports = {
|
|||
]
|
||||
});
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.(glb|gltf)$/,
|
||||
use: ["file-loader"],
|
||||
include: path.resolve(__dirname, "../")
|
||||
});
|
||||
|
||||
return config;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -11,7 +11,7 @@ import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons/faExcla
|
|||
import { faTimes } from "@fortawesome/free-solid-svg-icons/faTimes";
|
||||
import { Column } from "../layout/Column";
|
||||
|
||||
export function RevokeTokenModal({ onClose, revoke }) {
|
||||
export function RevokeTokenModal({ onClose, onRevoke }) {
|
||||
return (
|
||||
<Modal
|
||||
title={<FormattedMessage id="revoke-token-modal.title" defaultMessage="Revoke token" />}
|
||||
|
@ -51,7 +51,7 @@ export function RevokeTokenModal({ onClose, revoke }) {
|
|||
<Button preset="basic" sm onClick={onClose}>
|
||||
<FormattedMessage id="revoke-token-modal.cancel" defaultMessage="Cancel" />
|
||||
</Button>
|
||||
<Button preset="accent1" sm onClick={revoke}>
|
||||
<Button preset="accent1" sm onClick={onRevoke}>
|
||||
<FormattedMessage id="revoke-token-modal.revoke" defaultMessage="Revoke" />
|
||||
</Button>
|
||||
</Row>
|
||||
|
@ -62,5 +62,5 @@ export function RevokeTokenModal({ onClose, revoke }) {
|
|||
|
||||
RevokeTokenModal.propTypes = {
|
||||
onClose: PropTypes.func.isRequired,
|
||||
revoke: PropTypes.func.isRequired
|
||||
onRevoke: PropTypes.func.isRequired
|
||||
};
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import React from "react";
|
||||
import { CenteredModalWrapper } from "../layout/CenteredModalWrapper";
|
||||
import styles from "./Tokens.scss";
|
||||
import { TokenList } from "./TokenList";
|
||||
import { TokenPageLayout } from "./TokenPageLayout";
|
||||
import { NoAccess } from "./NoAccess";
|
||||
import { RevokeTokenModal } from "./RevokeTokenModal";
|
||||
import { RevealTokenModal } from "./RevealTokenModal";
|
||||
import { CreateToken } from "./CreateToken";
|
||||
import { StorybookAuthContextProvider } from "../auth/AuthContext";
|
||||
|
||||
export default {
|
||||
title: "Token/Tokens"
|
||||
|
@ -71,26 +71,35 @@ const dummyTokens = [
|
|||
|
||||
const noop = () => {};
|
||||
|
||||
// eslint-disable-next-line react/prop-types
|
||||
const StorybookWrapper = ({ children }) => {
|
||||
return (
|
||||
<StorybookAuthContextProvider>
|
||||
<TokenPageLayout>{children}</TokenPageLayout>
|
||||
</StorybookAuthContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const NoAccessPage = () => {
|
||||
return (
|
||||
<TokenPageLayout className={styles.backgroundGray}>
|
||||
<StorybookWrapper>
|
||||
<NoAccess />
|
||||
</TokenPageLayout>
|
||||
</StorybookWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react/prop-types
|
||||
export const TokenListPage = ({ children }) => (
|
||||
<TokenPageLayout>
|
||||
<StorybookWrapper>
|
||||
{children}
|
||||
<TokenList tokens={dummyTokens} onRevokeToken={noop} />
|
||||
</TokenPageLayout>
|
||||
</StorybookWrapper>
|
||||
);
|
||||
|
||||
export const EmptyTokenListPage = () => (
|
||||
<TokenPageLayout>
|
||||
<StorybookWrapper>
|
||||
<TokenList tokens={[]} onRevokeToken={noop} />
|
||||
</TokenPageLayout>
|
||||
</StorybookWrapper>
|
||||
);
|
||||
|
||||
export const RevokeTokenModalPage = () => (
|
||||
|
@ -109,17 +118,17 @@ export function ModalRevokeToken() {
|
|||
const selectedScopes = ["read_rooms", "write_rooms"];
|
||||
// eslint-disable-next-line react/prop-types
|
||||
export const CreateTokenPage = ({ children }) => (
|
||||
<TokenPageLayout>
|
||||
<StorybookWrapper>
|
||||
{children}
|
||||
<CreateToken scopes={scopes} scopeInfo={scopeInfo} selectedScopes={selectedScopes} />
|
||||
</TokenPageLayout>
|
||||
</StorybookWrapper>
|
||||
);
|
||||
|
||||
export const ModalSaveTokenPage = () => (
|
||||
<TokenPageLayout>
|
||||
<StorybookWrapper>
|
||||
<CenteredModalWrapper>
|
||||
<RevealTokenModal onClose={noop} token={{ token: "abcd1234" }} selectedScopes={["write_rooms"]} />
|
||||
</CenteredModalWrapper>
|
||||
<TokenList tokens={dummyTokens} onRevokeToken={noop} />
|
||||
</TokenPageLayout>
|
||||
</StorybookWrapper>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import React, { useState, useEffect, useContext } from "react";
|
||||
import { Token } from "./Token";
|
||||
import { fetchMyTokens } from "./token-utils";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import styles from "./Token.scss";
|
||||
import { RevokeTokenModal } from "./RevokeTokenModal";
|
||||
import { RevealTokenModal } from "./RevealTokenModal";
|
||||
import { TokenList } from "./TokenList";
|
||||
import { NoAccess } from "./NoAccess";
|
||||
import { CenteredModalWrapper } from "../layout/CenteredModalWrapper";
|
||||
import { AuthContext } from "../auth/AuthContext";
|
||||
|
||||
export function TokensContainer() {
|
||||
const [tokens, setTokens] = useState([]);
|
||||
// const [showRevealTokenModal, setRevealTokenModal] = useState(false);
|
||||
// const [showRevokeTokenModal, setShowRevokeTokenModal] = useState(false);
|
||||
// const [selectedRevokeId, setSelectedRevokeId] = useState();
|
||||
const auth = useContext(AuthContext); // Re-render when you log in/out.
|
||||
|
||||
useEffect(() => {
|
||||
// async function updateTokens() {
|
||||
// setTokens(await fetchMyTokens());
|
||||
// }
|
||||
// updateTokens();
|
||||
}, []);
|
||||
|
||||
const onRevealTokenModalClose = async ({ createdNewToken }) => {
|
||||
// setRevealTokenModal(false);
|
||||
// if (createdNewToken) {
|
||||
// setTokens(await fetchMyTokens());
|
||||
// }
|
||||
};
|
||||
|
||||
const onRevokeTokenClose = ({ removedTokenId }) => {
|
||||
// if (removedTokenId) setTokens(tokens.filter(token => token.id !== removedTokenId));
|
||||
// setShowRevokeTokenModal(false);
|
||||
// setSelectedRevokeId("");
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
// {showRevealTokenModal && (
|
||||
// <CenteredModalWrapper>
|
||||
// <RevealTokenModal onClose={onRevealTokenModalClose} />
|
||||
// </CenteredModalWrapper>
|
||||
// )}
|
||||
// {showRevokeTokenModal && (
|
||||
// <CenteredModalWrapper>
|
||||
// <RevokeTokenModal selectedId={selectedRevokeId} onClose={onRevokeTokenClose} />
|
||||
// </CenteredModalWrapper>
|
||||
// )}
|
||||
}
|
||||
|
||||
<button
|
||||
as="a"
|
||||
preset="primary"
|
||||
onClick={() => {
|
||||
// if (!showRevealTokenModal) setRevealTokenModal(true);
|
||||
}}
|
||||
>
|
||||
<FormattedMessage id="tokens.create-token" defaultMessage="Create Token" />
|
||||
</button>
|
||||
{auth?.isAdmin ? <TokenList /> : <NoAccess />}
|
||||
|
||||
{tokens.map(t => {
|
||||
return (
|
||||
<Token
|
||||
showRevokeToken={id => {
|
||||
// if (!showRevokeTokenModal) {
|
||||
// setSelectedRevokeId(id);
|
||||
// setShowRevokeTokenModal(true);
|
||||
// }
|
||||
}}
|
||||
key={t.id}
|
||||
{...t}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
import { fetchReticulumAuthenticated, getReticulumFetchUrl } from "../../utils/phoenix-utils.js";
|
||||
|
||||
const ENDPOINT = "/api/v2_alpha/credentials";
|
||||
const CREDENTIALS_ENDPOINT_URL = getReticulumFetchUrl(ENDPOINT);
|
||||
|
||||
export function fetchMyTokens() {
|
||||
return fetchReticulumAuthenticated(ENDPOINT).then(function(tokens) {
|
||||
console.log(tokens);
|
||||
return tokens.credentials;
|
||||
});
|
||||
}
|
||||
|
||||
function getHeaders() {
|
||||
return {
|
||||
"content-type": "application/json",
|
||||
authorization: `bearer ${window.APP.store.state.credentials.token}`
|
||||
};
|
||||
}
|
||||
|
||||
export async function createToken({ scopes }) {
|
||||
console.log(scopes);
|
||||
const res = await fetch(CREDENTIALS_ENDPOINT_URL, {
|
||||
headers: getHeaders(),
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
scopes,
|
||||
subjec_type: "account"
|
||||
})
|
||||
});
|
||||
if (res.ok) {
|
||||
return res.json();
|
||||
} else {
|
||||
console.log(res.status);
|
||||
console.log(res.statusText);
|
||||
throw new Error(res.statusText);
|
||||
}
|
||||
// TODO handle error case
|
||||
}
|
||||
|
||||
export async function revokeToken({ id }) {
|
||||
console.log("trying revoke...");
|
||||
const res = await fetch(`${CREDENTIALS_ENDPOINT_URL}/${id}?revoke`, {
|
||||
headers: getHeaders(),
|
||||
method: "PUT",
|
||||
body: JSON.stringify({
|
||||
id,
|
||||
revoke: true
|
||||
})
|
||||
});
|
||||
if (res.ok) {
|
||||
return res.json();
|
||||
} else {
|
||||
console.log(res.status);
|
||||
console.log(res.statusText);
|
||||
throw new Error(res.statusText);
|
||||
}
|
||||
}
|
||||
|
||||
export function fetchAvailableScopes() {
|
||||
// TODO turn into a fetch
|
||||
// fetch(`${CREDENTIALS_ENDPOINT}/scopes`, {
|
||||
// headers: getHeaders()
|
||||
// })
|
||||
return ["read_rooms", "write_rooms"];
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<!-- DO NOT REMOVE/EDIT THIS COMMENT - META_TAGS -->
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
|
||||
<link rel="shortcut icon" type="image/png" href="/favicon.ico">
|
||||
<title>Tokens</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,700;1,400&display=swap" rel="stylesheet">
|
||||
<script>
|
||||
if (navigator.doNotTrack !== "1") {
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBeforea,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="ui-root"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { WrappedIntlProvider } from "./react-components/wrapped-intl-provider";
|
||||
import registerTelemetry from "./telemetry";
|
||||
import Store from "./storage/store";
|
||||
import "./utils/theme";
|
||||
import { AuthContextProvider } from "./react-components/auth/AuthContext";
|
||||
import { TokensContainer } from "./react-components/tokens/TokensContainer";
|
||||
import "./assets/stylesheets/globals.scss";
|
||||
import "./react-components/styles/global.scss";
|
||||
import { TokenPageLayout } from "./react-components/tokens/TokenPageLayout";
|
||||
import configs from "./utils/configs";
|
||||
import { ThemeProvider } from "./react-components/styles/theme";
|
||||
|
||||
registerTelemetry("/tokens", "Backend API Tokens Page");
|
||||
|
||||
const store = new Store();
|
||||
window.APP = { store };
|
||||
|
||||
function Root() {
|
||||
return (
|
||||
<WrappedIntlProvider>
|
||||
<ThemeProvider store={store}>
|
||||
<AuthContextProvider store={store}>
|
||||
<TokenPageLayout>{(true || configs.feature("public_api_access")) && <TokensContainer />}</TokenPageLayout>
|
||||
</AuthContextProvider>
|
||||
</ThemeProvider>
|
||||
</WrappedIntlProvider>
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.render(<Root />, document.getElementById("ui-root"));
|
|
@ -269,6 +269,7 @@ module.exports = async (env, argv) => {
|
|||
cloud: path.join(__dirname, "src", "cloud.js"),
|
||||
signin: path.join(__dirname, "src", "signin.js"),
|
||||
verify: path.join(__dirname, "src", "verify.js"),
|
||||
tokens: path.join(__dirname, "src", "tokens.js"),
|
||||
"whats-new": path.join(__dirname, "src", "whats-new.js")
|
||||
},
|
||||
output: {
|
||||
|
@ -293,6 +294,7 @@ module.exports = async (env, argv) => {
|
|||
{ from: /^\/discord/, to: "/discord.html" },
|
||||
{ from: /^\/cloud/, to: "/cloud.html" },
|
||||
{ from: /^\/verify/, to: "/verify.html" },
|
||||
{ from: /^\/tokens/, to: "/tokens.html" },
|
||||
{ from: /^\/whats-new/, to: "/whats-new.html" }
|
||||
]
|
||||
},
|
||||
|
@ -601,6 +603,14 @@ module.exports = async (env, argv) => {
|
|||
removeComments: false
|
||||
}
|
||||
}),
|
||||
new HTMLWebpackPlugin({
|
||||
filename: "tokens.html",
|
||||
template: path.join(__dirname, "src", "tokens.html"),
|
||||
chunks: ["tokens"],
|
||||
minify: {
|
||||
removeComments: false
|
||||
}
|
||||
}),
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: "src/hub.service.js",
|
||||
|
|
Загрузка…
Ссылка в новой задаче