mirror of
https://github.com/lightninglabs/lightning-terminal.git
synced 2026-08-13 12:33:36 +02:00
Merge pull request #345 from lightninglabs/read-only-session
Add support for read-only macaroons
This commit is contained in:
commit
c0f0b309cf
8 changed files with 109 additions and 12 deletions
|
|
@ -58,6 +58,7 @@ interface Props {
|
|||
extra?: ReactNode;
|
||||
placeholder?: string;
|
||||
onChange?: (value: string) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const FormSelect: React.FC<Props> = ({
|
||||
|
|
@ -66,17 +67,17 @@ const FormSelect: React.FC<Props> = ({
|
|||
value,
|
||||
placeholder,
|
||||
onChange,
|
||||
className,
|
||||
}) => {
|
||||
const { Wrapper, Select } = Styled;
|
||||
return (
|
||||
<Wrapper>
|
||||
<Wrapper className={className}>
|
||||
<Select
|
||||
value={value}
|
||||
onChange={v => onChange && onChange(v as string)}
|
||||
placeholder={placeholder}
|
||||
aria-label={label}
|
||||
options={options}
|
||||
dropdownClassName="asdf"
|
||||
/>
|
||||
</Wrapper>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ import React, { useCallback, useState } from 'react';
|
|||
import { observer } from 'mobx-react-lite';
|
||||
import styled from '@emotion/styled';
|
||||
import { usePrefixedTranslation } from 'hooks';
|
||||
import * as LIT from 'types/generated/lit-sessions_pb';
|
||||
import { MAX_DATE } from 'util/constants';
|
||||
import { useStore } from 'store';
|
||||
import { Button, Column, HeaderFour, Row } from 'components/base';
|
||||
import FormField from 'components/common/FormField';
|
||||
import FormInput from 'components/common/FormInput';
|
||||
import FormSelect from 'components/common/FormSelect';
|
||||
import PurpleButton from './PurpleButton';
|
||||
|
||||
const Styled = {
|
||||
|
|
@ -21,6 +23,17 @@ const Styled = {
|
|||
padding: 12px 16px;
|
||||
}
|
||||
`,
|
||||
FormSelect: styled(FormSelect)`
|
||||
.rc-select {
|
||||
font-family: ${props => props.theme.fonts.open.regular};
|
||||
font-size: ${props => props.theme.sizes.m};
|
||||
padding: 12px 40px 8px 0px;
|
||||
}
|
||||
|
||||
.rc-select-selection-item {
|
||||
padding-left: 14px;
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
interface Props {
|
||||
|
|
@ -32,18 +45,25 @@ const AddSession: React.FC<Props> = ({ primary }) => {
|
|||
const { sessionStore } = useStore();
|
||||
|
||||
const [label, setLabel] = useState('');
|
||||
const [permissions, setPermissions] = useState('admin');
|
||||
const [editing, setEditing] = useState(false);
|
||||
|
||||
const toggleEditing = useCallback(() => setEditing(e => !e), []);
|
||||
const handleSubmit = useCallback(async () => {
|
||||
const session = await sessionStore.addSession(label, MAX_DATE);
|
||||
const sessionType =
|
||||
permissions === 'admin'
|
||||
? LIT.SessionType.TYPE_MACAROON_ADMIN
|
||||
: LIT.SessionType.TYPE_MACAROON_READONLY;
|
||||
|
||||
const session = await sessionStore.addSession(label, sessionType, MAX_DATE);
|
||||
|
||||
if (session) {
|
||||
setLabel('');
|
||||
setEditing(false);
|
||||
}
|
||||
}, [label]);
|
||||
}, [label, permissions]);
|
||||
|
||||
const { Wrapper, FormHeader, FormInput } = Styled;
|
||||
const { Wrapper, FormHeader, FormInput, FormSelect } = Styled;
|
||||
if (!editing) {
|
||||
return (
|
||||
<PurpleButton tertiary={!primary} onClick={toggleEditing}>
|
||||
|
|
@ -54,13 +74,32 @@ const AddSession: React.FC<Props> = ({ primary }) => {
|
|||
|
||||
return (
|
||||
<Wrapper>
|
||||
<FormHeader>{l('label')}</FormHeader>
|
||||
<Row>
|
||||
<Column>
|
||||
<FormHeader>{l('label')}</FormHeader>
|
||||
</Column>
|
||||
<Column>
|
||||
<FormHeader>{l('permissions')}</FormHeader>
|
||||
</Column>
|
||||
</Row>
|
||||
<Row>
|
||||
<Column cols={6}>
|
||||
<FormField>
|
||||
<FormInput value={label} onChange={setLabel} placeholder={l('labelHint')} />
|
||||
</FormField>
|
||||
</Column>
|
||||
<Column>
|
||||
<FormField>
|
||||
<FormSelect
|
||||
value={permissions}
|
||||
onChange={setPermissions}
|
||||
options={[
|
||||
{ label: 'Admin', value: 'admin' },
|
||||
{ label: 'Read Only', value: 'read-only' },
|
||||
]}
|
||||
/>
|
||||
</FormField>
|
||||
</Column>
|
||||
<Column>
|
||||
<PurpleButton onClick={handleSubmit}>{l('common.submit')}</PurpleButton>
|
||||
<Button ghost borderless onClick={toggleEditing}>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { Global, Theme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useStore } from 'store';
|
||||
import { Background, Menu } from 'components/base';
|
||||
|
|
@ -10,6 +11,47 @@ interface CollapsedProps {
|
|||
fullWidth?: boolean;
|
||||
}
|
||||
|
||||
const GlobalStyles = (theme: Theme) => `
|
||||
.rc-select-dropdown {
|
||||
padding-top: 10px;
|
||||
background-color: transparent;
|
||||
|
||||
& > div {
|
||||
color: ${theme.colors.offWhite};
|
||||
background-color: ${theme.colors.lightBlue};
|
||||
border-width: 0;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 16px 16px rgba(0, 0, 0, 0.15);
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.rc-select-item {
|
||||
color: ${theme.colors.white};
|
||||
font-family: ${theme.fonts.open.regular};
|
||||
font-weight: 600;
|
||||
font-size: ${theme.sizes.s};
|
||||
line-height: 24px;
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid ${theme.colors.paleBlue};
|
||||
|
||||
&:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: ${theme.colors.white};
|
||||
background-color: ${theme.colors.blue};
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
& > .rc-select-item-option-state {
|
||||
top: 16px;
|
||||
right: 12px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Styled = {
|
||||
Container: styled.div<{ fullWidth: boolean }>`
|
||||
position: relative;
|
||||
|
|
@ -86,6 +128,7 @@ export const Layout: React.FC = ({ children }) => {
|
|||
<Fluid className="container-fluid">{children}</Fluid>
|
||||
</Content>
|
||||
</Container>
|
||||
<Global styles={GlobalStyles} />
|
||||
</Background>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ const theme: Theme = {
|
|||
purple: '#57038d',
|
||||
overlay: 'rgba(245,245,245,0.04)',
|
||||
gradient: 'linear-gradient(325.53deg, #252F4A 0%, #46547B 100%);',
|
||||
lightBlue: '#384770',
|
||||
paleBlue: '#2E3A5C',
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
2
app/src/emotion-theme.d.ts
vendored
2
app/src/emotion-theme.d.ts
vendored
|
|
@ -38,6 +38,8 @@ declare module '@emotion/react' {
|
|||
purple: string;
|
||||
overlay: string;
|
||||
gradient: string;
|
||||
lightBlue: string;
|
||||
paleBlue: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
"cmps.common.Wizard.backTip": "Back to Previous",
|
||||
"cmps.connect.AddSession.create": "Create a new session",
|
||||
"cmps.connect.AddSession.label": "Label",
|
||||
"cmps.connect.AddSession.permissions": "Permissions",
|
||||
"cmps.connect.AddSession.labelHint": "My First Session",
|
||||
"cmps.connect.AddSession.expiration": "Expiration",
|
||||
"cmps.connect.AddSession.expirationSuffix": "",
|
||||
|
|
|
|||
|
|
@ -51,11 +51,11 @@ export default class Session {
|
|||
get typeLabel() {
|
||||
switch (this.type) {
|
||||
case LIT.SessionType.TYPE_MACAROON_READONLY:
|
||||
return 'Readonly Macaroon';
|
||||
return 'Read-Only';
|
||||
case LIT.SessionType.TYPE_MACAROON_ADMIN:
|
||||
return 'Admin Macaroon';
|
||||
return 'Admin';
|
||||
case LIT.SessionType.TYPE_MACAROON_CUSTOM:
|
||||
return 'Custom Macaroon';
|
||||
return 'Custom';
|
||||
case LIT.SessionType.TYPE_UI_PASSWORD:
|
||||
return 'LiT UI Password';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,7 +83,11 @@ export default class SessionStore {
|
|||
s.label.startsWith('Default Session'),
|
||||
).length;
|
||||
const countText = count === 0 ? '' : `(${count})`;
|
||||
await this.addSession(`Default Session ${countText}`, MAX_DATE);
|
||||
await this.addSession(
|
||||
`Default Session ${countText}`,
|
||||
LIT.SessionType.TYPE_MACAROON_ADMIN,
|
||||
MAX_DATE,
|
||||
);
|
||||
}
|
||||
} catch (error: any) {
|
||||
this._store.appView.handleError(error, 'Unable to fetch sessions');
|
||||
|
|
@ -93,11 +97,16 @@ export default class SessionStore {
|
|||
/**
|
||||
* Adds a new session
|
||||
* @param label the user defined label for this session
|
||||
* @param type the type of session being created (admin, read-only, etc)
|
||||
* @param expiry how long the session should be valid for
|
||||
* @param mailboxServerAddr the address where the mailbox server is reachable
|
||||
* @param devServer whether the mailbox server is a dev server that has no valid TLS cert
|
||||
*/
|
||||
async addSession(label: string, expiry: Date) {
|
||||
async addSession(
|
||||
label: string,
|
||||
type: LIT.SessionTypeMap[keyof LIT.SessionTypeMap],
|
||||
expiry: Date,
|
||||
) {
|
||||
try {
|
||||
this._store.log.info(`submitting session with label ${label}`, {
|
||||
expiry,
|
||||
|
|
@ -107,7 +116,7 @@ export default class SessionStore {
|
|||
|
||||
const { session } = await this._store.api.lit.addSession(
|
||||
label,
|
||||
LIT.SessionType.TYPE_UI_PASSWORD,
|
||||
type,
|
||||
expiry,
|
||||
this.proxyServer,
|
||||
!IS_PROD,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue