New login screen (#1331)

* wip

* feat: first draft of auth.config

* chore: auth methodS

* chore: styling

* styling, styling, styling

* feat: Auth notices

* chore: Remove server-rendered pages, move shared/components -> components

* lint

* cleanup

* cleanup

* fix: Remove unused component

* fix: Ensure env variables in prod too

* style tweaks

* fix: Entering SSO email into login form fails
fix: Tweak language around guest signin
This commit is contained in:
Tom Moor 2020-07-09 22:33:07 -07:00 committed by GitHub
parent 75561079eb
commit 5cb04d7ac1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
128 changed files with 769 additions and 2264 deletions

View File

@ -126,7 +126,6 @@ Backend is driven by [Koa](http://koajs.com/) (API, web server), [Sequelize](htt
- `server/commands` - Domain logic, currently being refactored from /models
- `server/emails` - React rendered email templates
- `server/models` - Database models
- `server/pages` - Server-side rendered public pages
- `server/policies` - Authorization logic
- `server/presenters` - API responses for database models
- `server/test` - Test helps and support

View File

@ -1,7 +1,7 @@
// @flow
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
export const Action = styled(Flex)`
justify-content: center;

View File

@ -1,36 +0,0 @@
// @flow
import * as React from "react";
import { observer } from "mobx-react";
import Flex from "shared/components/Flex";
import styled from "styled-components";
type Props = {
children: React.Node,
type?: "info" | "success" | "warning" | "danger" | "offline",
};
@observer
class Alert extends React.Component<Props> {
defaultProps = {
type: "info",
};
render() {
return (
<Container align="center" justify="center" type={this.props.type}>
{this.props.children}
</Container>
);
}
}
const Container = styled(Flex)`
height: $headerHeight;
color: ${props => props.theme.white};
font-size: 14px;
line-height: 1;
background-color: ${({ theme, type }) => theme.color[type]};
`;
export default Alert;

View File

@ -1,6 +1,7 @@
// @flow
import * as React from "react";
import { observer, inject } from "mobx-react";
import { Redirect } from "react-router-dom";
import AuthStore from "stores/AuthStore";
import LoadingIndicator from "components/LoadingIndicator";
import { isCustomSubdomain } from "shared/utils/domains";
@ -35,7 +36,7 @@ const Authenticated = observer(({ auth, children }: Props) => {
}
auth.logout(true);
return null;
return <Redirect to="/" />;
});
export default inject("auth")(Authenticated);

View File

@ -9,7 +9,7 @@ import { PadlockIcon, GoToIcon, MoreIcon } from "outline-icons";
import Document from "models/Document";
import CollectionsStore from "stores/CollectionsStore";
import { collectionUrl } from "utils/routeHelpers";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import BreadcrumbMenu from "./BreadcrumbMenu";
import CollectionIcon from "components/CollectionIcon";

View File

@ -5,7 +5,8 @@ import { darken, lighten } from "polished";
import { ExpandedIcon } from "outline-icons";
const RealButton = styled.button`
display: inline-block;
display: ${props => (props.fullwidth ? "block" : "inline-block")};
width: ${props => (props.fullwidth ? "100%" : "auto")};
margin: 0;
padding: 0;
border: 0;
@ -126,6 +127,7 @@ export type Props = {
children?: React.Node,
innerRef?: React.ElementRef<any>,
disclosure?: boolean,
fullwidth?: boolean,
borderOnHover?: boolean,
};

View File

@ -0,0 +1,13 @@
// @flow
import styled from "styled-components";
import Button, { Inner } from "./Button";
const ButtonLarge = styled(Button)`
height: 40px;
${Inner} {
padding: 4px 16px;
}
`;
export default ButtonLarge;

View File

@ -11,7 +11,7 @@ import { DEFAULT_PAGINATION_LIMIT } from "stores/BaseStore";
import DocumentsStore from "stores/DocumentsStore";
import RevisionsStore from "stores/RevisionsStore";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import { ListPlaceholder } from "components/LoadingPlaceholder";
import Revision from "./components/Revision";
import { documentHistoryUrl } from "utils/routeHelpers";

View File

@ -5,8 +5,8 @@ import styled, { withTheme } from "styled-components";
import format from "date-fns/format";
import { MoreIcon } from "outline-icons";
import Flex from "shared/components/Flex";
import Time from "shared/components/Time";
import Flex from "components/Flex";
import Time from "components/Time";
import Avatar from "components/Avatar";
import RevisionMenu from "menus/RevisionMenu";
import Document from "models/Document";

View File

@ -4,7 +4,7 @@ import { observer } from "mobx-react";
import { Link } from "react-router-dom";
import { StarredIcon } from "outline-icons";
import styled, { withTheme } from "styled-components";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Badge from "components/Badge";
import Tooltip from "components/Tooltip";
import Highlight from "components/Highlight";

View File

@ -7,7 +7,7 @@ import { PortalWithState } from "react-portal";
import { MoreIcon } from "outline-icons";
import { rgba } from "polished";
import styled from "styled-components";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import { fadeAndScaleIn } from "shared/styles/animations";
import NudeButton from "components/NudeButton";

View File

@ -2,7 +2,7 @@
import * as React from "react";
import { observer, inject } from "mobx-react";
import styled, { withTheme } from "styled-components";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Avatar from "components/Avatar";
import User from "models/User";

View File

@ -5,7 +5,7 @@ import { observable } from "mobx";
import { observer, inject } from "mobx-react";
import { MAX_AVATAR_DISPLAY } from "shared/constants";
import Modal from "components/Modal";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Facepile from "components/Facepile";
import GroupMembers from "scenes/GroupMembers";
import ListItem from "components/List/Item";

View File

@ -27,7 +27,7 @@ import styled from "styled-components";
import { LabelText } from "components/Input";
import { DropdownMenu } from "components/DropdownMenu";
import NudeButton from "components/NudeButton";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
export const icons = {
collection: {

View File

@ -4,7 +4,7 @@ import { observer } from "mobx-react";
import { observable } from "mobx";
import styled from "styled-components";
import VisuallyHidden from "components/VisuallyHidden";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
const RealTextarea = styled.textarea`
border: 0;

View File

@ -0,0 +1,13 @@
// @flow
import styled from "styled-components";
import Input from "./Input";
const InputLarge = styled(Input)`
height: 40px;
input {
height: 40px;
}
`;
export default InputLarge;

View File

@ -1,7 +1,7 @@
// @flow
import * as React from "react";
import { observer } from "mobx-react";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import styled from "styled-components";
type Props = {

View File

@ -8,7 +8,7 @@ import { observable } from "mobx";
import { observer, inject } from "mobx-react";
import keydown from "react-keydown";
import Analytics from "components/Analytics";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import {
homeUrl,
searchUrl,

View File

@ -1,7 +1,7 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
type Props = {
image?: React.Node,

View File

@ -4,7 +4,7 @@ import { times } from "lodash";
import styled from "styled-components";
import Mask from "components/Mask";
import Fade from "components/Fade";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
type Props = {
count?: number,

View File

@ -4,7 +4,7 @@ import { times } from "lodash";
import styled from "styled-components";
import Mask from "components/Mask";
import Fade from "components/Fade";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
type Props = {
count?: number,

View File

@ -3,7 +3,7 @@ import * as React from "react";
import styled from "styled-components";
import Mask from "components/Mask";
import Fade from "components/Fade";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
export default function LoadingPlaceholder(props: Object) {
return (

View File

@ -3,7 +3,7 @@ import * as React from "react";
import styled from "styled-components";
import { pulsate } from "shared/styles/animations";
import { randomInteger } from "shared/random";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
type Props = {
header?: boolean,

View File

@ -8,7 +8,7 @@ import { transparentize } from "polished";
import { CloseIcon, BackIcon } from "outline-icons";
import NudeButton from "components/NudeButton";
import { fadeAndScaleIn } from "shared/styles/animations";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
ReactModal.setAppElement("#root");

12
app/components/Notice.js Normal file
View File

@ -0,0 +1,12 @@
// @flow
import styled from "styled-components";
const Notice = styled.p`
background: ${props => props.theme.sidebarBackground};
color: ${props => props.theme.sidebarText};
padding: 10px 12px;
border-radius: 4px;
position: relative;
`;
export default Notice;

View File

@ -0,0 +1,24 @@
// @flow
import * as React from "react";
import Notice from "components/Notice";
export default function AlertNotice({ children }: { children: React.Node }) {
return (
<Notice muted>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style={{ position: "relative", top: "2px" }}
>
<path
d="M15.6676 11.5372L10.0155 1.14735C9.10744 -0.381434 6.89378 -0.383465 5.98447 1.14735L0.332715 11.5372C-0.595598 13.0994 0.528309 15.0776 2.34778 15.0776H13.652C15.47 15.0776 16.5959 13.101 15.6676 11.5372ZM8 13.2026C7.48319 13.2026 7.0625 12.7819 7.0625 12.2651C7.0625 11.7483 7.48319 11.3276 8 11.3276C8.51681 11.3276 8.9375 11.7483 8.9375 12.2651C8.9375 12.7819 8.51681 13.2026 8 13.2026ZM8.9375 9.45257C8.9375 9.96938 8.51681 10.3901 8 10.3901C7.48319 10.3901 7.0625 9.96938 7.0625 9.45257V4.76507C7.0625 4.24826 7.48319 3.82757 8 3.82757C8.51681 3.82757 8.9375 4.24826 8.9375 4.76507V9.45257Z"
fill="currentColor"
/>
</svg>{" "}
{children}
</Notice>
);
}

View File

@ -3,7 +3,7 @@ import * as React from "react";
import { observer } from "mobx-react";
import styled from "styled-components";
import { GoToIcon } from "outline-icons";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Document from "models/Document";
import Collection from "models/Collection";

View File

@ -3,9 +3,9 @@ import * as React from "react";
import { inject, observer } from "mobx-react";
import styled from "styled-components";
import Document from "models/Document";
import Flex from "shared/components/Flex";
import Time from "shared/components/Time";
import Breadcrumb from "shared/components/Breadcrumb";
import Flex from "components/Flex";
import Time from "components/Time";
import Breadcrumb from "components/Breadcrumb";
import CollectionsStore from "stores/CollectionsStore";
import AuthStore from "stores/AuthStore";

View File

@ -12,7 +12,7 @@ import {
PlusIcon,
} from "outline-icons";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Modal from "components/Modal";
import Invite from "scenes/Invite";
import AccountMenu from "menus/AccountMenu";

View File

@ -19,7 +19,7 @@ import {
import ZapierIcon from "./icons/Zapier";
import SlackIcon from "./icons/Slack";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Sidebar from "./Sidebar";
import Scrollable from "components/Scrollable";
import Section from "./components/Section";
@ -54,7 +54,7 @@ class SettingsSidebar extends React.Component<Props> {
<HeaderBlock
subheading={
<ReturnToApp align="center">
<BackIcon /> Return to App
<BackIcon color="currentColor" /> Return to App
</ReturnToApp>
}
teamName={team.name}

View File

@ -7,7 +7,7 @@ import breakpoint from "styled-components-breakpoint";
import { observer, inject } from "mobx-react";
import { CloseIcon, MenuIcon } from "outline-icons";
import Fade from "components/Fade";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import UiStore from "stores/UiStore";
let firstRender = true;

View File

@ -11,7 +11,7 @@ import SidebarLink from "./SidebarLink";
import DocumentLink from "./DocumentLink";
import CollectionIcon from "components/CollectionIcon";
import DropToImport from "components/DropToImport";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
type Props = {
collection: Collection,

View File

@ -3,7 +3,7 @@ import * as React from "react";
import { observer, inject } from "mobx-react";
import { withRouter, type RouterHistory } from "react-router-dom";
import keydown from "react-keydown";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import { PlusIcon } from "outline-icons";
import { newDocumentUrl } from "utils/routeHelpers";

View File

@ -10,7 +10,7 @@ import DropToImport from "components/DropToImport";
import Fade from "components/Fade";
import Collection from "models/Collection";
import DocumentsStore from "stores/DocumentsStore";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import { type NavigationNode } from "types";
type Props = {

View File

@ -1,5 +1,5 @@
// @flow
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import styled from "styled-components";
const Header = styled(Flex)`

View File

@ -2,8 +2,8 @@
import * as React from "react";
import styled, { withTheme } from "styled-components";
import { ExpandedIcon } from "outline-icons";
import Flex from "shared/components/Flex";
import TeamLogo from "shared/components/TeamLogo";
import Flex from "components/Flex";
import TeamLogo from "components/TeamLogo";
type Props = {
teamName: string,

View File

@ -1,6 +1,6 @@
// @flow
import styled from "styled-components";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
const Section = styled(Flex)`
position: relative;

View File

@ -5,7 +5,7 @@ import { observer } from "mobx-react";
import { withRouter, NavLink } from "react-router-dom";
import { CollapsedIcon } from "outline-icons";
import styled, { withTheme } from "styled-components";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
type Props = {
to?: string | Object,

View File

@ -2,10 +2,10 @@
import styled from "styled-components";
const TeamLogo = styled.img`
width: 38px;
width: auto;
height: 38px;
border-radius: 4px;
background: ${props => props.theme.white};
background: ${props => props.theme.background};
border: 1px solid ${props => props.theme.divider};
`;

View File

@ -7,7 +7,7 @@ import { SunIcon, MoonIcon } from "outline-icons";
import styled from "styled-components";
import UiStore from "stores/UiStore";
import AuthStore from "stores/AuthStore";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import { DropdownMenu, DropdownMenuItem } from "components/DropdownMenu";
import Modal from "components/Modal";
import KeyboardShortcuts from "scenes/KeyboardShortcuts";

View File

@ -1,7 +1,7 @@
// @flow
import * as React from "react";
import { Switch, Route, Redirect } from "react-router-dom";
import Home from "scenes/Home";
import Login from "scenes/Login";
import Dashboard from "scenes/Dashboard";
import Starred from "scenes/Starred";
import Drafts from "scenes/Drafts";
@ -38,7 +38,8 @@ const RedirectDocument = ({ match }: { match: Object }) => (
export default function Routes() {
return (
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/" component={Login} />
<Route exact path="/create" component={Login} />
<Route exact path="/share/:shareId" component={KeyedDocument} />
<Authenticated>
<SocketProvider>

View File

@ -30,7 +30,7 @@ import HelpText from "components/HelpText";
import DocumentList from "components/DocumentList";
import Subheading from "components/Subheading";
import PageTitle from "components/PageTitle";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Modal from "components/Modal";
import CollectionMembers from "scenes/CollectionMembers";
import Tabs from "components/Tabs";

View File

@ -5,7 +5,7 @@ import { observable } from "mobx";
import { inject, observer } from "mobx-react";
import { homeUrl } from "utils/routeHelpers";
import Button from "components/Button";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import HelpText from "components/HelpText";
import Collection from "models/Collection";
import CollectionsStore from "stores/CollectionsStore";

View File

@ -6,7 +6,7 @@ import Input from "components/Input";
import InputRich from "components/InputRich";
import Button from "components/Button";
import Switch from "components/Switch";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import HelpText from "components/HelpText";
import IconPicker from "components/IconPicker";
import Collection from "models/Collection";

View File

@ -3,7 +3,7 @@ import * as React from "react";
import { observable } from "mobx";
import { inject, observer } from "mobx-react";
import Button from "components/Button";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import HelpText from "components/HelpText";
import Collection from "models/Collection";
import AuthStore from "stores/AuthStore";

View File

@ -5,7 +5,7 @@ import { inject, observer } from "mobx-react";
import { observable } from "mobx";
import { debounce } from "lodash";
import Button from "components/Button";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import HelpText from "components/HelpText";
import Input from "components/Input";
import Modal from "components/Modal";

View File

@ -3,7 +3,7 @@ import * as React from "react";
import { inject, observer } from "mobx-react";
import { observable } from "mobx";
import { debounce } from "lodash";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import HelpText from "components/HelpText";
import Input from "components/Input";
import Modal from "components/Modal";

View File

@ -4,7 +4,7 @@ import { observable } from "mobx";
import styled from "styled-components";
import { inject, observer } from "mobx-react";
import { PlusIcon } from "outline-icons";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import HelpText from "components/HelpText";
import Subheading from "components/Subheading";
import Button from "components/Button";

View File

@ -2,8 +2,8 @@
import * as React from "react";
import styled from "styled-components";
import Avatar from "components/Avatar";
import Flex from "shared/components/Flex";
import Time from "shared/components/Time";
import Flex from "components/Flex";
import Time from "components/Time";
import Badge from "components/Badge";
import Button from "components/Button";
import InputSelect from "components/InputSelect";

View File

@ -1,7 +1,7 @@
// @flow
import * as React from "react";
import { PlusIcon } from "outline-icons";
import Time from "shared/components/Time";
import Time from "components/Time";
import Avatar from "components/Avatar";
import Button from "components/Button";
import Badge from "components/Badge";

View File

@ -10,7 +10,7 @@ import Input from "components/Input";
import InputRich from "components/InputRich";
import IconPicker, { icons } from "components/IconPicker";
import HelpText from "components/HelpText";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Collection from "models/Collection";
import CollectionsStore from "stores/CollectionsStore";

View File

@ -1,6 +1,6 @@
// @flow
import styled from "styled-components";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
const Container = styled(Flex)`
position: relative;

View File

@ -8,7 +8,7 @@ import { observer, inject } from "mobx-react";
import { Prompt, Route, withRouter } from "react-router-dom";
import type { Location, RouterHistory } from "react-router-dom";
import keydown from "react-keydown";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import {
collectionUrl,
documentMoveUrl,
@ -29,9 +29,9 @@ import MarkAsViewed from "./MarkAsViewed";
import ErrorBoundary from "components/ErrorBoundary";
import LoadingIndicator from "components/LoadingIndicator";
import PageTitle from "components/PageTitle";
import Branding from "shared/components/Branding";
import Notice from "shared/components/Notice";
import Time from "shared/components/Time";
import Branding from "components/Branding";
import Notice from "components/Notice";
import Time from "components/Time";
import UiStore from "stores/UiStore";
import AuthStore from "stores/AuthStore";

View File

@ -12,7 +12,7 @@ import Modal from "components/Modal";
import Input from "components/Input";
import Labeled from "components/Labeled";
import PathToDocument from "components/PathToDocument";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Document from "models/Document";
import DocumentsStore from "stores/DocumentsStore";

View File

@ -5,7 +5,7 @@ import Textarea from "react-autosize-textarea";
import { observer } from "mobx-react";
import Editor from "components/Editor";
import ClickablePadding from "components/ClickablePadding";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import parseTitle from "shared/utils/parseTitle";
import Document from "models/Document";
import DocumentMeta from "./DocumentMeta";

View File

@ -13,8 +13,8 @@ import AuthStore from "stores/AuthStore";
import { documentEditUrl } from "utils/routeHelpers";
import { meta } from "utils/keyboard";
import Flex from "shared/components/Flex";
import Breadcrumb, { Slash } from "shared/components/Breadcrumb";
import Flex from "components/Flex";
import Breadcrumb, { Slash } from "components/Breadcrumb";
import DocumentMenu from "menus/DocumentMenu";
import NewChildDocumentMenu from "menus/NewChildDocumentMenu";
import DocumentShare from "scenes/DocumentShare";

View File

@ -4,7 +4,7 @@ import { withRouter, type RouterHistory } from "react-router-dom";
import { observable } from "mobx";
import { inject, observer } from "mobx-react";
import Button from "components/Button";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import HelpText from "components/HelpText";
import Document from "models/Document";
import DocumentsStore from "stores/DocumentsStore";

View File

@ -2,7 +2,7 @@
import * as React from "react";
import { inject } from "mobx-react";
import type { RouterHistory, Location } from "react-router-dom";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import CenteredContent from "components/CenteredContent";
import LoadingPlaceholder from "components/LoadingPlaceholder";
import DocumentsStore from "stores/DocumentsStore";

View File

@ -5,7 +5,7 @@ import { observable } from "mobx";
import { inject, observer } from "mobx-react";
import { groupSettings } from "shared/utils/routeHelpers";
import Button from "components/Button";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import HelpText from "components/HelpText";
import Group from "models/Group";
import UiStore from "stores/UiStore";

View File

@ -6,7 +6,7 @@ import { inject, observer } from "mobx-react";
import Button from "components/Button";
import Input from "components/Input";
import HelpText from "components/HelpText";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Group from "models/Group";
import UiStore from "stores/UiStore";

View File

@ -3,7 +3,7 @@ import * as React from "react";
import { inject, observer } from "mobx-react";
import { observable } from "mobx";
import { debounce } from "lodash";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import HelpText from "components/HelpText";
import Input from "components/Input";
import Modal from "components/Modal";

View File

@ -3,7 +3,7 @@ import * as React from "react";
import { observable } from "mobx";
import { inject, observer } from "mobx-react";
import { PlusIcon } from "outline-icons";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Empty from "components/Empty";
import HelpText from "components/HelpText";
import Subheading from "components/Subheading";

View File

@ -1,8 +1,8 @@
// @flow
import * as React from "react";
import Avatar from "components/Avatar";
import Flex from "shared/components/Flex";
import Time from "shared/components/Time";
import Flex from "components/Flex";
import Time from "components/Time";
import Badge from "components/Badge";
import Button from "components/Button";
import ListItem from "components/List/Item";

View File

@ -1,7 +1,7 @@
// @flow
import * as React from "react";
import { PlusIcon } from "outline-icons";
import Time from "shared/components/Time";
import Time from "components/Time";
import Avatar from "components/Avatar";
import Button from "components/Button";
import Badge from "components/Badge";

View File

@ -8,7 +8,7 @@ import Input from "components/Input";
import HelpText from "components/HelpText";
import Modal from "components/Modal";
import GroupMembers from "scenes/GroupMembers";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Group from "models/Group";
import GroupsStore from "stores/GroupsStore";

View File

@ -1,17 +0,0 @@
// @flow
import * as React from "react";
import { observer, inject } from "mobx-react";
import { Redirect } from "react-router-dom";
import AuthStore from "stores/AuthStore";
type Props = {
auth: AuthStore,
};
const Home = observer(({ auth }: Props) => {
if (auth.authenticated) return <Redirect to="/home" />;
auth.logout(true);
return null;
});
export default inject("auth")(Home);

View File

@ -3,13 +3,12 @@ import * as React from "react";
import { Link, withRouter, type RouterHistory } from "react-router-dom";
import { observable, action } from "mobx";
import { inject, observer } from "mobx-react";
import { CloseIcon } from "outline-icons";
import { LinkIcon, CloseIcon } from "outline-icons";
import styled from "styled-components";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Button from "components/Button";
import Input from "components/Input";
import CopyToClipboard from "components/CopyToClipboard";
import Checkbox from "components/Checkbox";
import HelpText from "components/HelpText";
import Tooltip from "components/Tooltip";
import NudeButton from "components/NudeButton";
@ -33,7 +32,6 @@ type Props = {
type InviteRequest = {
email: string,
name: string,
guest: boolean,
};
@observer
@ -42,9 +40,9 @@ class Invite extends React.Component<Props> {
@observable linkCopied: boolean = false;
@observable
invites: InviteRequest[] = [
{ email: "", name: "", guest: false },
{ email: "", name: "", guest: false },
{ email: "", name: "", guest: false },
{ email: "", name: "" },
{ email: "", name: "" },
{ email: "", name: "" },
];
handleSubmit = async (ev: SyntheticEvent<>) => {
@ -80,7 +78,7 @@ class Invite extends React.Component<Props> {
);
}
this.invites.push({ email: "", name: "", guest: false });
this.invites.push({ email: "", name: "" });
};
@action
@ -106,8 +104,8 @@ class Invite extends React.Component<Props> {
{team.guestSignin ? (
<HelpText>
Invite team members or guests to join your knowledge base. Team
members can sign in with {team.signinMethods} and guests can use
their email address.
members can sign in with {team.signinMethods} or use their email
address.
</HelpText>
) : (
<HelpText>
@ -116,22 +114,35 @@ class Invite extends React.Component<Props> {
{can.update && (
<React.Fragment>
As an admin you can also{" "}
<Link to="/settings/security">enable guest invites</Link>.
<Link to="/settings/security">enable email sign-in</Link>.
</React.Fragment>
)}
</HelpText>
)}
{team.subdomain && (
<CopyBlock>
Want a link to share directly with your team?
<Flex>
<Input type="text" value={team.url} readOnly flex />&nbsp;&nbsp;
<Flex align="flex-end">
<Input
type="text"
value={team.url}
label="Want a link to share directly with your team?"
readOnly
flex
/>&nbsp;&nbsp;
<CopyToClipboard text={team.url} onCopy={this.handleCopy}>
<Button type="button" neutral>
<Button
type="button"
icon={<LinkIcon />}
style={{ marginBottom: "16px" }}
neutral
>
{this.linkCopied ? "Link copied" : "Copy link"}
</Button>
</CopyToClipboard>
</Flex>
<p>
<hr />
</p>
</CopyBlock>
)}
{this.invites.map((invite, index) => (
@ -159,29 +170,6 @@ class Invite extends React.Component<Props> {
required={!!invite.email}
flex
/>
{team.guestSignin && (
<React.Fragment>
&nbsp;&nbsp;
<Tooltip
tooltip={
<span>
Guests can sign in with email and <br />do not require{" "}
{team.signinMethods} accounts
</span>
}
placement="top"
>
<Guest>
<Checkbox
name="guest"
label="Guest"
onChange={ev => this.handleGuestChange(ev, index)}
checked={invite.guest}
/>
</Guest>
</Tooltip>
</React.Fragment>
)}
{index !== 0 && (
<Remove>
<Tooltip tooltip="Remove invite" placement="top">
@ -220,22 +208,8 @@ class Invite extends React.Component<Props> {
}
const CopyBlock = styled("div")`
margin: 2em 0;
font-size: 14px;
background: ${props => props.theme.secondaryBackground};
padding: 8px 16px 4px;
border-radius: 8px;
margin-bottom: 24px;
input {
background: ${props => props.theme.background};
border-radius: 4px;
}
`;
const Guest = styled("div")`
padding-top: 4px;
margin: 0 4px 16px;
align-self: flex-end;
`;
const Remove = styled("div")`

View File

@ -2,7 +2,7 @@
import * as React from "react";
import styled from "styled-components";
import Key from "components/Key";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import HelpText from "components/HelpText";
import { meta } from "utils/keyboard";

View File

@ -1,61 +1,55 @@
// @flow
import * as React from "react";
import Notice from "../../../shared/components/Notice";
import NoticeAlert from "components/NoticeAlert";
type Props = {
notice?: string,
};
export default function AuthNotices({ notice }: Props) {
export default function Notices({ notice }: Props) {
return (
<React.Fragment>
{notice === "guest-success" && (
<Notice>
A magic sign-in link has been sent to your email address, no password
needed.
</Notice>
)}
{notice === "google-hd" && (
<Notice>
<NoticeAlert>
Sorry, Google sign in cannot be used with a personal email. Please try
signing in with your company Google account.
</Notice>
signing in with your company GSuite account.
</NoticeAlert>
)}
{notice === "hd-not-allowed" && (
<Notice>
<NoticeAlert>
Sorry, your Google apps domain is not allowed. Please try again with
an allowed company domain.
</Notice>
</NoticeAlert>
)}
{notice === "email-auth-required" && (
<Notice>
<NoticeAlert>
Your account uses email sign-in, please sign-in with email to
continue.
</Notice>
</NoticeAlert>
)}
{notice === "email-auth-ratelimit" && (
<Notice>
An email sign-in link was recently sent, please check your inbox and
<NoticeAlert>
An email sign-in link was recently sent, please check your inbox or
try again in a few minutes.
</Notice>
</NoticeAlert>
)}
{notice === "auth-error" && (
<Notice>
<NoticeAlert>
Authentication failed - we were unable to sign you in at this time.
Please try again.
</Notice>
</NoticeAlert>
)}
{notice === "expired-token" && (
<Notice>
<NoticeAlert>
Sorry, it looks like that sign-in link is no longer valid, please try
requesting another.
</Notice>
</NoticeAlert>
)}
{notice === "suspended" && (
<Notice>
<NoticeAlert>
Your Outline account has been suspended. To re-activate your account,
please contact a team admin.
</Notice>
</NoticeAlert>
)}
</React.Fragment>
);

147
app/scenes/Login/Service.js Normal file
View File

@ -0,0 +1,147 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import { EmailIcon } from "outline-icons";
import { client } from "utils/ApiClient";
import ButtonLarge from "components/ButtonLarge";
import SlackLogo from "components/SlackLogo";
import GoogleLogo from "components/GoogleLogo";
import InputLarge from "components/InputLarge";
type Props = {
id: string,
name: string,
authUrl: string,
isCreate: boolean,
onEmailSuccess: (email: string) => void,
};
type State = {
showEmailSignin: boolean,
isSubmitting: boolean,
email: string,
};
class Service extends React.Component<Props, State> {
state = {
showEmailSignin: false,
isSubmitting: false,
email: "",
};
handleChangeEmail = (event: SyntheticInputEvent<HTMLInputElement>) => {
this.setState({ email: event.target.value });
};
handleSubmitEmail = async (event: SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
if (this.state.showEmailSignin && this.state.email) {
this.setState({ isSubmitting: true });
try {
const response = await client.post(event.currentTarget.action, {
email: this.state.email,
});
if (response.redirect) {
window.location.href = response.redirect;
} else {
this.props.onEmailSuccess(this.state.email);
}
} finally {
this.setState({ isSubmitting: false });
}
} else {
this.setState({ showEmailSignin: true });
}
};
render() {
const { isCreate, id, name, authUrl } = this.props;
if (id === "email") {
if (isCreate) {
return null;
}
return (
<Wrapper key="email">
<Form
method="POST"
action="/auth/email"
onSubmit={this.handleSubmitEmail}
>
{this.state.showEmailSignin ? (
<React.Fragment>
<InputLarge
type="email"
name="email"
placeholder="me@domain.com"
value={this.state.email}
onChange={this.handleChangeEmail}
disabled={this.state.isSubmitting}
autoFocus
required
short
/>
<ButtonLarge type="submit" disabled={this.state.isSubmitting}>
Sign In
</ButtonLarge>
</React.Fragment>
) : (
<ButtonLarge type="submit" icon={<EmailIcon />} fullwidth>
Continue with Email
</ButtonLarge>
)}
</Form>
</Wrapper>
);
}
const icon =
id === "slack" ? (
<Logo>
<SlackLogo size={16} />
</Logo>
) : id === "google" ? (
<Logo>
<GoogleLogo size={16} />
</Logo>
) : (
undefined
);
return (
<Wrapper key={id}>
<ButtonLarge
onClick={() => (window.location.href = authUrl)}
icon={icon}
fullwidth
>
{isCreate ? "Sign up" : "Continue"} with {name}
</ButtonLarge>
</Wrapper>
);
}
}
const Logo = styled.div`
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
`;
const Wrapper = styled.div`
margin-bottom: 1em;
width: 100%;
`;
const Form = styled.form`
width: 100%;
display: flex;
justify-content: space-between;
`;
export default Service;

230
app/scenes/Login/index.js Normal file
View File

@ -0,0 +1,230 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import { BackIcon, EmailIcon } from "outline-icons";
import { observer, inject } from "mobx-react";
import { Redirect } from "react-router-dom";
import { find } from "lodash";
import Flex from "components/Flex";
import TeamLogo from "components/TeamLogo";
import OutlineLogo from "components/OutlineLogo";
import Heading from "components/Heading";
import PageTitle from "components/PageTitle";
import ButtonLarge from "components/ButtonLarge";
import HelpText from "components/HelpText";
import Fade from "components/Fade";
import Service from "./Service";
import Notices from "./Notices";
import AuthStore from "stores/AuthStore";
import getQueryVariable from "shared/utils/getQueryVariable";
type Props = {
auth: AuthStore,
location: Object,
};
type State = {
emailLinkSentTo: string,
};
@observer
class Login extends React.Component<Props, State> {
state = {
emailLinkSentTo: "",
};
handleReset = () => {
this.setState({ emailLinkSentTo: "" });
};
handleEmailSuccess = email => {
this.setState({ emailLinkSentTo: email });
};
render() {
const { auth, location } = this.props;
const { config } = auth;
const isCreate = location.pathname === "/create";
if (auth.authenticated) {
return <Redirect to="/home" />;
}
// we're counting on the config request being fast
if (!config) {
return null;
}
const hasMultipleServices = config.services.length > 1;
const defaultService = find(
config.services,
service => service.id === auth.lastSignedIn
);
const header =
process.env.DEPLOYMENT === "hosted" &&
(config.hostname ? (
<Back href={process.env.URL}>
<BackIcon color="currentColor" /> Back to home
</Back>
) : (
<Back href="https://www.getoutline.com">
<BackIcon color="currentColor" /> Back to website
</Back>
));
if (this.state.emailLinkSentTo) {
return (
<Background>
{header}
<Centered align="center" justify="center" column auto>
<PageTitle title="Check your email" />
<CheckEmailIcon size={38} color="currentColor" />
<Heading>Check your email</Heading>
<Note>
A magic sign-in link has been sent to the email{" "}
<em>{this.state.emailLinkSentTo}</em>, no password needed.
</Note>
<br />
<ButtonLarge onClick={this.handleReset} fullwidth neutral>
Back to login
</ButtonLarge>
</Centered>
</Background>
);
}
return (
<Background>
{header}
<Centered align="center" justify="center" column auto>
<PageTitle title="Login" />
<Logo>
{process.env.TEAM_LOGO && process.env.DEPLOYMENT !== "hosted" ? (
<TeamLogo src={process.env.TEAM_LOGO} />
) : (
<OutlineLogo size={38} fill="currentColor" />
)}
</Logo>
{isCreate ? (
<Heading>Create an account</Heading>
) : (
<Heading>Login to {config.name || "Outline"}</Heading>
)}
<Notices notice={getQueryVariable("notice")} />
{defaultService && (
<React.Fragment key={defaultService.id}>
<Service
isCreate={isCreate}
onEmailSuccess={this.handleEmailSuccess}
{...defaultService}
/>
{hasMultipleServices && (
<React.Fragment>
<Note>
You signed in with {defaultService.name} last time.
</Note>
<Or />
</React.Fragment>
)}
</React.Fragment>
)}
{config.services.map(service => {
if (service.id === auth.lastSignedIn) {
return null;
}
return (
<Service
key={service.id}
isCreate={isCreate}
onEmailSuccess={this.handleEmailSuccess}
{...service}
/>
);
})}
</Centered>
</Background>
);
}
}
const CheckEmailIcon = styled(EmailIcon)`
margin-bottom: -1.5em;
`;
const Background = styled(Fade)`
width: 100vw;
height: 100vh;
background: ${props => props.theme.background};
display: flex;
`;
const Logo = styled.div`
margin-bottom: -1.5em;
height: 38px;
`;
const Note = styled(HelpText)`
text-align: center;
font-size: 14px;
em {
font-style: normal;
font-weight: 500;
}
`;
const Back = styled.a`
display: flex;
align-items: center;
color: inherit;
padding: 32px;
font-weight: 500;
position: absolute;
svg {
transition: transform 100ms ease-in-out;
}
&:hover {
svg {
transform: translateX(-4px);
}
}
`;
const Or = styled.hr`
margin: 1em 0;
position: relative;
width: 100%;
&:after {
content: "Or";
display: block;
position: absolute;
left: 50%;
transform: translate3d(-50%, -50%, 0);
text-transform: uppercase;
font-size: 11px;
color: ${props => props.theme.textSecondary};
background: ${props => props.theme.background};
border-radius: 2px;
padding: 0 4px;
}
`;
const Centered = styled(Flex)`
user-select: none;
width: 90vw;
height: 100%;
max-width: 320px;
margin: 0 auto;
`;
export default inject("auth")(Login);

View File

@ -19,7 +19,7 @@ import UsersStore from "stores/UsersStore";
import { newDocumentUrl, searchUrl } from "utils/routeHelpers";
import { meta } from "utils/keyboard";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Button from "components/Button";
import Empty from "components/Empty";
import Fade from "components/Fade";

View File

@ -3,7 +3,7 @@ import * as React from "react";
import { CheckmarkIcon } from "outline-icons";
import styled from "styled-components";
import HelpText from "components/HelpText";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
type Props = {
label: string,

View File

@ -2,7 +2,7 @@
import * as React from "react";
import styled, { withTheme } from "styled-components";
import { SearchIcon } from "outline-icons";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
type Props = {
onChange: string => void,

View File

@ -12,7 +12,7 @@ import Button from "components/Button";
import CenteredContent from "components/CenteredContent";
import PageTitle from "components/PageTitle";
import HelpText from "components/HelpText";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
type Props = {
auth: AuthStore,

View File

@ -9,7 +9,7 @@ import HelpText from "components/HelpText";
import Input from "components/Input";
import Subheading from "components/Subheading";
import NotificationListItem from "./components/NotificationListItem";
import Notice from "shared/components/Notice";
import Notice from "components/Notice";
import UiStore from "stores/UiStore";
import AuthStore from "stores/AuthStore";

View File

@ -12,7 +12,7 @@ import Button from "components/Button";
import CenteredContent from "components/CenteredContent";
import PageTitle from "components/PageTitle";
import UserDelete from "scenes/UserDelete";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
type Props = {
auth: AuthStore,

View File

@ -60,8 +60,6 @@ class Security extends React.Component<Props> {
}, 500);
render() {
const { team } = this.props.auth;
return (
<CenteredContent>
<PageTitle title="Security" />
@ -72,27 +70,25 @@ class Security extends React.Component<Props> {
</HelpText>
<Checkbox
label="Allow guest invites"
label="Allow email authentication"
name="guestSignin"
checked={this.guestSignin}
onChange={this.handleChange}
note={`When enabled guests can be invited by email address and are able to signin without ${
team ? team.signinMethods : "SSO"
}`}
note="When enabled, users can sign-in using their email address"
/>
<Checkbox
label="Public document sharing"
name="sharing"
checked={this.sharing}
onChange={this.handleChange}
note="When enabled documents can be shared publicly by any team member"
note="When enabled, documents can be shared publicly on the internet by any team member"
/>
<Checkbox
label="Rich service embeds"
name="documentEmbeds"
checked={this.documentEmbeds}
onChange={this.handleChange}
note="Convert links to supported services into rich embeds within your documents"
note="Links to supported services are shown as rich embeds within your documents"
/>
</CenteredContent>
);

View File

@ -12,7 +12,7 @@ import SlackButton from "./components/SlackButton";
import CollectionsStore from "stores/CollectionsStore";
import IntegrationsStore from "stores/IntegrationsStore";
import AuthStore from "stores/AuthStore";
import Notice from "shared/components/Notice";
import Notice from "components/Notice";
import getQueryVariable from "shared/utils/getQueryVariable";
type Props = {

View File

@ -3,7 +3,7 @@ import * as React from "react";
import { Link } from "react-router-dom";
import { capitalize } from "lodash";
import styled from "styled-components";
import Time from "shared/components/Time";
import Time from "components/Time";
import ListItem from "components/List/Item";
import Avatar from "components/Avatar";
import Event from "models/Event";

View File

@ -5,7 +5,7 @@ import { observer, inject } from "mobx-react";
import styled from "styled-components";
import Dropzone from "react-dropzone";
import LoadingIndicator from "components/LoadingIndicator";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import Modal from "components/Modal";
import Button from "components/Button";
import AvatarEditor from "react-avatar-editor";

View File

@ -2,7 +2,7 @@
import * as React from "react";
import ShareMenu from "menus/ShareMenu";
import ListItem from "components/List/Item";
import Time from "shared/components/Time";
import Time from "components/Time";
import Share from "models/Share";
type Props = {

View File

@ -2,7 +2,7 @@
import * as React from "react";
import styled from "styled-components";
import { slackAuth } from "shared/utils/routeHelpers";
import SlackLogo from "shared/components/SlackLogo";
import SlackLogo from "components/SlackLogo";
import Button from "components/Button";
type Props = {

View File

@ -8,7 +8,7 @@ import Avatar from "components/Avatar";
import Badge from "components/Badge";
import UserProfile from "scenes/UserProfile";
import ListItem from "components/List/Item";
import Time from "shared/components/Time";
import Time from "components/Time";
import User from "models/User";
type Props = {

View File

@ -3,7 +3,7 @@ import * as React from "react";
import { observable } from "mobx";
import { inject, observer } from "mobx-react";
import Button from "components/Button";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import HelpText from "components/HelpText";
import Modal from "components/Modal";
import AuthStore from "stores/AuthStore";

View File

@ -5,7 +5,7 @@ import distanceInWordsToNow from "date-fns/distance_in_words_to_now";
import { inject, observer } from "mobx-react";
import { withRouter, type RouterHistory } from "react-router-dom";
import { EditIcon } from "outline-icons";
import Flex from "shared/components/Flex";
import Flex from "components/Flex";
import HelpText from "components/HelpText";
import Modal from "components/Modal";
import Button from "components/Button";

View File

@ -10,13 +10,27 @@ import Team from "models/Team";
const AUTH_STORE = "AUTH_STORE";
type Service = {
id: string,
name: string,
authUrl: string,
};
type Config = {
name?: string,
hostname?: string,
services: Service[],
};
export default class AuthStore {
@observable user: ?User;
@observable team: ?Team;
@observable token: ?string;
@observable lastSignedIn: ?string;
@observable isSaving: boolean = false;
@observable isSuspended: boolean = false;
@observable suspendedContactEmail: ?string;
@observable config: ?Config;
rootStore: RootStore;
constructor(rootStore: RootStore) {
@ -32,8 +46,12 @@ export default class AuthStore {
this.user = new User(data.user);
this.team = new Team(data.team);
this.token = getCookie("accessToken");
this.lastSignedIn = getCookie("lastSignedIn");
setImmediate(() => this.fetchConfig());
if (this.token) setImmediate(() => this.fetch());
if (this.token) {
setImmediate(() => this.fetch());
}
autorun(() => {
try {
@ -63,6 +81,13 @@ export default class AuthStore {
});
}
@action
fetchConfig = async () => {
const res = await client.post("/auth.config");
invariant(res && res.data, "Config not available");
this.config = res.data;
};
@action
fetch = async () => {
try {
@ -158,10 +183,16 @@ export default class AuthStore {
})
);
this.token = null;
// if this logout was forced from an authenticated route then
// save the current path so we can go back there once signed in
if (savePath) {
setCookie("postLoginRedirectPath", window.location.pathname);
const pathName = window.location.pathname;
if (pathName !== "/" && pathName !== "/create") {
setCookie("postLoginRedirectPath", pathName);
}
}
// remove authentication token itself
@ -178,8 +209,5 @@ export default class AuthStore {
});
this.team = null;
}
// add a timestamp to force reload from server
window.location.href = `${BASE_URL}?done=${new Date().getTime()}`;
};
}

View File

@ -34,6 +34,7 @@ class ApiClient {
) => {
let body;
let modifiedPath;
let urlToFetch;
if (method === "GET") {
if (data) {
@ -45,6 +46,12 @@ class ApiClient {
body = data ? JSON.stringify(data) : undefined;
}
if (path.match(/^http/)) {
urlToFetch = modifiedPath || path;
} else {
urlToFetch = this.baseUrl + (modifiedPath || path);
}
// Construct headers
const headers = new Headers({
Accept: "application/json",
@ -60,7 +67,7 @@ class ApiClient {
let response;
try {
response = await fetch(this.baseUrl + (modifiedPath || path), {
response = await fetch(urlToFetch, {
method,
body,
headers,

View File

@ -1,144 +0,0 @@
// flow-typed signature: d7c6f4d4223c61be85becba99f8e5712
// flow-typed version: <<STUB>>/styled-components-grid_v^1.0.0-preview.15/flow_v0.71.0
/**
* This is an autogenerated libdef stub for:
*
* 'styled-components-grid'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'styled-components-grid' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'styled-components-grid/dist/cjs/components/Grid' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/cjs/components/GridUnit' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/cjs/components/index' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/cjs/index' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/cjs/mixins/grid' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/cjs/mixins/gridUnit' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/cjs/mixins/index' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/es/components/Grid' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/es/components/GridUnit' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/es/components/index' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/es/index' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/es/mixins/grid' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/es/mixins/gridUnit' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/es/mixins/index' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/example/index' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/styled-components-grid' {
declare module.exports: any;
}
declare module 'styled-components-grid/dist/styled-components-grid.min' {
declare module.exports: any;
}
// Filename aliases
declare module 'styled-components-grid/dist/cjs/components/Grid.js' {
declare module.exports: $Exports<'styled-components-grid/dist/cjs/components/Grid'>;
}
declare module 'styled-components-grid/dist/cjs/components/GridUnit.js' {
declare module.exports: $Exports<'styled-components-grid/dist/cjs/components/GridUnit'>;
}
declare module 'styled-components-grid/dist/cjs/components/index.js' {
declare module.exports: $Exports<'styled-components-grid/dist/cjs/components/index'>;
}
declare module 'styled-components-grid/dist/cjs/index.js' {
declare module.exports: $Exports<'styled-components-grid/dist/cjs/index'>;
}
declare module 'styled-components-grid/dist/cjs/mixins/grid.js' {
declare module.exports: $Exports<'styled-components-grid/dist/cjs/mixins/grid'>;
}
declare module 'styled-components-grid/dist/cjs/mixins/gridUnit.js' {
declare module.exports: $Exports<'styled-components-grid/dist/cjs/mixins/gridUnit'>;
}
declare module 'styled-components-grid/dist/cjs/mixins/index.js' {
declare module.exports: $Exports<'styled-components-grid/dist/cjs/mixins/index'>;
}
declare module 'styled-components-grid/dist/es/components/Grid.js' {
declare module.exports: $Exports<'styled-components-grid/dist/es/components/Grid'>;
}
declare module 'styled-components-grid/dist/es/components/GridUnit.js' {
declare module.exports: $Exports<'styled-components-grid/dist/es/components/GridUnit'>;
}
declare module 'styled-components-grid/dist/es/components/index.js' {
declare module.exports: $Exports<'styled-components-grid/dist/es/components/index'>;
}
declare module 'styled-components-grid/dist/es/index.js' {
declare module.exports: $Exports<'styled-components-grid/dist/es/index'>;
}
declare module 'styled-components-grid/dist/es/mixins/grid.js' {
declare module.exports: $Exports<'styled-components-grid/dist/es/mixins/grid'>;
}
declare module 'styled-components-grid/dist/es/mixins/gridUnit.js' {
declare module.exports: $Exports<'styled-components-grid/dist/es/mixins/gridUnit'>;
}
declare module 'styled-components-grid/dist/es/mixins/index.js' {
declare module.exports: $Exports<'styled-components-grid/dist/es/mixins/index'>;
}
declare module 'styled-components-grid/dist/example/index.js' {
declare module.exports: $Exports<'styled-components-grid/dist/example/index'>;
}
declare module 'styled-components-grid/dist/styled-components-grid.js' {
declare module.exports: $Exports<'styled-components-grid/dist/styled-components-grid'>;
}
declare module 'styled-components-grid/dist/styled-components-grid.min.js' {
declare module.exports: $Exports<'styled-components-grid/dist/styled-components-grid.min'>;
}

View File

@ -1,12 +1,12 @@
// @flow
require('./init');
require("./init");
if (
!process.env.SECRET_KEY ||
process.env.SECRET_KEY === 'generate_a_new_key'
process.env.SECRET_KEY === "generate_a_new_key"
) {
console.error(
'The SECRET_KEY env variable must be set with the output of `openssl rand -hex 32`'
"The SECRET_KEY env variable must be set with the output of `openssl rand -hex 32`"
);
// $FlowFixMe
process.exit(1);
@ -14,11 +14,11 @@ if (
if (process.env.AWS_ACCESS_KEY_ID) {
[
'AWS_REGION',
'AWS_SECRET_ACCESS_KEY',
'AWS_S3_UPLOAD_BUCKET_URL',
'AWS_S3_UPLOAD_BUCKET_NAME',
'AWS_S3_UPLOAD_MAX_SIZE',
"AWS_REGION",
"AWS_SECRET_ACCESS_KEY",
"AWS_S3_UPLOAD_BUCKET_URL",
"AWS_S3_UPLOAD_BUCKET_NAME",
"AWS_S3_UPLOAD_MAX_SIZE",
].forEach(key => {
if (!process.env[key]) {
console.error(`The ${key} env variable must be set when using AWS`);
@ -40,7 +40,7 @@ if (process.env.SLACK_KEY) {
if (!process.env.URL) {
console.error(
'The URL env variable must be set to the externally accessible URL, e.g (https://www.getoutline.com)'
"The URL env variable must be set to the externally accessible URL, e.g (https://www.getoutline.com)"
);
// $FlowFixMe
process.exit(1);
@ -48,7 +48,7 @@ if (!process.env.URL) {
if (!process.env.DATABASE_URL) {
console.error(
'The DATABASE_URL env variable must be set to the location of your postgres server, including authentication and port'
"The DATABASE_URL env variable must be set to the location of your postgres server, including authentication and port"
);
// $FlowFixMe
process.exit(1);
@ -56,7 +56,7 @@ if (!process.env.DATABASE_URL) {
if (!process.env.REDIS_URL) {
console.error(
'The REDIS_URL env variable must be set to the location of your redis server, including authentication and port'
"The REDIS_URL env variable must be set to the location of your redis server, including authentication and port"
);
// $FlowFixMe
process.exit(1);
@ -64,17 +64,17 @@ if (!process.env.REDIS_URL) {
if (!process.env.WEBSOCKETS_ENABLED) {
console.log(
'WARNING: Websockets are disabled. Set WEBSOCKETS_ENABLED env variable to true to enable'
"WARNING: Websockets are disabled. Set WEBSOCKETS_ENABLED env variable to true to enable"
);
}
if (process.env.NODE_ENV === 'production') {
console.log('\n\x1b[33m%s\x1b[0m', 'Running Outline in production mode.');
} else if (process.env.NODE_ENV === 'development') {
if (process.env.NODE_ENV === "production") {
console.log("\n\x1b[33m%s\x1b[0m", "Running Outline in production mode.");
} else if (process.env.NODE_ENV === "development") {
console.log(
'\n\x1b[33m%s\x1b[0m',
"\n\x1b[33m%s\x1b[0m",
'Running Outline in development mode with hot reloading. To run Outline in production mode set the NODE_ENV env variable to "production"'
);
}
require('./server');
require("./server");

View File

@ -1,4 +1,4 @@
// @flow
require('babel-core/register');
require('babel-polyfill');
require('dotenv').config({ silent: true });
require("babel-core/register");
require("babel-polyfill");
require("dotenv").config({ silent: true });

View File

@ -155,7 +155,6 @@
"string-replace-to-array": "^1.0.3",
"styled-components": "^5.0.0",
"styled-components-breakpoint": "^2.1.1",
"styled-components-grid": "^2.2.1",
"styled-normalize": "^8.0.4",
"tiny-cookie": "^2.3.1",
"tmp": "0.0.33",

Some files were not shown because too many files have changed in this diff Show More