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/commands` - Domain logic, currently being refactored from /models
- `server/emails` - React rendered email templates - `server/emails` - React rendered email templates
- `server/models` - Database models - `server/models` - Database models
- `server/pages` - Server-side rendered public pages
- `server/policies` - Authorization logic - `server/policies` - Authorization logic
- `server/presenters` - API responses for database models - `server/presenters` - API responses for database models
- `server/test` - Test helps and support - `server/test` - Test helps and support

View File

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

View File

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

View File

@ -5,7 +5,8 @@ import { darken, lighten } from "polished";
import { ExpandedIcon } from "outline-icons"; import { ExpandedIcon } from "outline-icons";
const RealButton = styled.button` const RealButton = styled.button`
display: inline-block; display: ${props => (props.fullwidth ? "block" : "inline-block")};
width: ${props => (props.fullwidth ? "100%" : "auto")};
margin: 0; margin: 0;
padding: 0; padding: 0;
border: 0; border: 0;
@ -126,6 +127,7 @@ export type Props = {
children?: React.Node, children?: React.Node,
innerRef?: React.ElementRef<any>, innerRef?: React.ElementRef<any>,
disclosure?: boolean, disclosure?: boolean,
fullwidth?: boolean,
borderOnHover?: 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 DocumentsStore from "stores/DocumentsStore";
import RevisionsStore from "stores/RevisionsStore"; import RevisionsStore from "stores/RevisionsStore";
import Flex from "shared/components/Flex"; import Flex from "components/Flex";
import { ListPlaceholder } from "components/LoadingPlaceholder"; import { ListPlaceholder } from "components/LoadingPlaceholder";
import Revision from "./components/Revision"; import Revision from "./components/Revision";
import { documentHistoryUrl } from "utils/routeHelpers"; import { documentHistoryUrl } from "utils/routeHelpers";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@ import { observer } from "mobx-react";
import { observable } from "mobx"; import { observable } from "mobx";
import styled from "styled-components"; import styled from "styled-components";
import VisuallyHidden from "components/VisuallyHidden"; import VisuallyHidden from "components/VisuallyHidden";
import Flex from "shared/components/Flex"; import Flex from "components/Flex";
const RealTextarea = styled.textarea` const RealTextarea = styled.textarea`
border: 0; 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 // @flow
import * as React from "react"; import * as React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import Flex from "shared/components/Flex"; import Flex from "components/Flex";
import styled from "styled-components"; import styled from "styled-components";
type Props = { type Props = {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ import { transparentize } from "polished";
import { CloseIcon, BackIcon } from "outline-icons"; import { CloseIcon, BackIcon } from "outline-icons";
import NudeButton from "components/NudeButton"; import NudeButton from "components/NudeButton";
import { fadeAndScaleIn } from "shared/styles/animations"; import { fadeAndScaleIn } from "shared/styles/animations";
import Flex from "shared/components/Flex"; import Flex from "components/Flex";
ReactModal.setAppElement("#root"); 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 { observer } from "mobx-react";
import styled from "styled-components"; import styled from "styled-components";
import { GoToIcon } from "outline-icons"; import { GoToIcon } from "outline-icons";
import Flex from "shared/components/Flex"; import Flex from "components/Flex";
import Document from "models/Document"; import Document from "models/Document";
import Collection from "models/Collection"; import Collection from "models/Collection";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,10 +2,10 @@
import styled from "styled-components"; import styled from "styled-components";
const TeamLogo = styled.img` const TeamLogo = styled.img`
width: 38px; width: auto;
height: 38px; height: 38px;
border-radius: 4px; border-radius: 4px;
background: ${props => props.theme.white}; background: ${props => props.theme.background};
border: 1px solid ${props => props.theme.divider}; 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 styled from "styled-components";
import UiStore from "stores/UiStore"; import UiStore from "stores/UiStore";
import AuthStore from "stores/AuthStore"; import AuthStore from "stores/AuthStore";
import Flex from "shared/components/Flex"; import Flex from "components/Flex";
import { DropdownMenu, DropdownMenuItem } from "components/DropdownMenu"; import { DropdownMenu, DropdownMenuItem } from "components/DropdownMenu";
import Modal from "components/Modal"; import Modal from "components/Modal";
import KeyboardShortcuts from "scenes/KeyboardShortcuts"; import KeyboardShortcuts from "scenes/KeyboardShortcuts";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@
import * as React from "react"; import * as React from "react";
import { inject } from "mobx-react"; import { inject } from "mobx-react";
import type { RouterHistory, Location } from "react-router-dom"; 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 CenteredContent from "components/CenteredContent";
import LoadingPlaceholder from "components/LoadingPlaceholder"; import LoadingPlaceholder from "components/LoadingPlaceholder";
import DocumentsStore from "stores/DocumentsStore"; import DocumentsStore from "stores/DocumentsStore";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ import Input from "components/Input";
import HelpText from "components/HelpText"; import HelpText from "components/HelpText";
import Modal from "components/Modal"; import Modal from "components/Modal";
import GroupMembers from "scenes/GroupMembers"; import GroupMembers from "scenes/GroupMembers";
import Flex from "shared/components/Flex"; import Flex from "components/Flex";
import Group from "models/Group"; import Group from "models/Group";
import GroupsStore from "stores/GroupsStore"; 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 { Link, withRouter, type RouterHistory } from "react-router-dom";
import { observable, action } from "mobx"; import { observable, action } from "mobx";
import { inject, observer } from "mobx-react"; import { inject, observer } from "mobx-react";
import { CloseIcon } from "outline-icons"; import { LinkIcon, CloseIcon } from "outline-icons";
import styled from "styled-components"; import styled from "styled-components";
import Flex from "shared/components/Flex"; import Flex from "components/Flex";
import Button from "components/Button"; import Button from "components/Button";
import Input from "components/Input"; import Input from "components/Input";
import CopyToClipboard from "components/CopyToClipboard"; import CopyToClipboard from "components/CopyToClipboard";
import Checkbox from "components/Checkbox";
import HelpText from "components/HelpText"; import HelpText from "components/HelpText";
import Tooltip from "components/Tooltip"; import Tooltip from "components/Tooltip";
import NudeButton from "components/NudeButton"; import NudeButton from "components/NudeButton";
@ -33,7 +32,6 @@ type Props = {
type InviteRequest = { type InviteRequest = {
email: string, email: string,
name: string, name: string,
guest: boolean,
}; };
@observer @observer
@ -42,9 +40,9 @@ class Invite extends React.Component<Props> {
@observable linkCopied: boolean = false; @observable linkCopied: boolean = false;
@observable @observable
invites: InviteRequest[] = [ invites: InviteRequest[] = [
{ email: "", name: "", guest: false }, { email: "", name: "" },
{ email: "", name: "", guest: false }, { email: "", name: "" },
{ email: "", name: "", guest: false }, { email: "", name: "" },
]; ];
handleSubmit = async (ev: SyntheticEvent<>) => { 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 @action
@ -106,8 +104,8 @@ class Invite extends React.Component<Props> {
{team.guestSignin ? ( {team.guestSignin ? (
<HelpText> <HelpText>
Invite team members or guests to join your knowledge base. Team Invite team members or guests to join your knowledge base. Team
members can sign in with {team.signinMethods} and guests can use members can sign in with {team.signinMethods} or use their email
their email address. address.
</HelpText> </HelpText>
) : ( ) : (
<HelpText> <HelpText>
@ -116,22 +114,35 @@ class Invite extends React.Component<Props> {
{can.update && ( {can.update && (
<React.Fragment> <React.Fragment>
As an admin you can also{" "} 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> </React.Fragment>
)} )}
</HelpText> </HelpText>
)} )}
{team.subdomain && ( {team.subdomain && (
<CopyBlock> <CopyBlock>
Want a link to share directly with your team? <Flex align="flex-end">
<Flex> <Input
<Input type="text" value={team.url} readOnly flex />&nbsp;&nbsp; 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}> <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"} {this.linkCopied ? "Link copied" : "Copy link"}
</Button> </Button>
</CopyToClipboard> </CopyToClipboard>
</Flex> </Flex>
<p>
<hr />
</p>
</CopyBlock> </CopyBlock>
)} )}
{this.invites.map((invite, index) => ( {this.invites.map((invite, index) => (
@ -159,29 +170,6 @@ class Invite extends React.Component<Props> {
required={!!invite.email} required={!!invite.email}
flex 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 && ( {index !== 0 && (
<Remove> <Remove>
<Tooltip tooltip="Remove invite" placement="top"> <Tooltip tooltip="Remove invite" placement="top">
@ -220,22 +208,8 @@ class Invite extends React.Component<Props> {
} }
const CopyBlock = styled("div")` const CopyBlock = styled("div")`
margin: 2em 0;
font-size: 14px; 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")` const Remove = styled("div")`

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -60,8 +60,6 @@ class Security extends React.Component<Props> {
}, 500); }, 500);
render() { render() {
const { team } = this.props.auth;
return ( return (
<CenteredContent> <CenteredContent>
<PageTitle title="Security" /> <PageTitle title="Security" />
@ -72,27 +70,25 @@ class Security extends React.Component<Props> {
</HelpText> </HelpText>
<Checkbox <Checkbox
label="Allow guest invites" label="Allow email authentication"
name="guestSignin" name="guestSignin"
checked={this.guestSignin} checked={this.guestSignin}
onChange={this.handleChange} onChange={this.handleChange}
note={`When enabled guests can be invited by email address and are able to signin without ${ note="When enabled, users can sign-in using their email address"
team ? team.signinMethods : "SSO"
}`}
/> />
<Checkbox <Checkbox
label="Public document sharing" label="Public document sharing"
name="sharing" name="sharing"
checked={this.sharing} checked={this.sharing}
onChange={this.handleChange} 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 <Checkbox
label="Rich service embeds" label="Rich service embeds"
name="documentEmbeds" name="documentEmbeds"
checked={this.documentEmbeds} checked={this.documentEmbeds}
onChange={this.handleChange} 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> </CenteredContent>
); );

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@ import * as React from "react";
import { observable } from "mobx"; import { observable } from "mobx";
import { inject, observer } from "mobx-react"; import { inject, observer } from "mobx-react";
import Button from "components/Button"; import Button from "components/Button";
import Flex from "shared/components/Flex"; import Flex from "components/Flex";
import HelpText from "components/HelpText"; import HelpText from "components/HelpText";
import Modal from "components/Modal"; import Modal from "components/Modal";
import AuthStore from "stores/AuthStore"; 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 { inject, observer } from "mobx-react";
import { withRouter, type RouterHistory } from "react-router-dom"; import { withRouter, type RouterHistory } from "react-router-dom";
import { EditIcon } from "outline-icons"; import { EditIcon } from "outline-icons";
import Flex from "shared/components/Flex"; import Flex from "components/Flex";
import HelpText from "components/HelpText"; import HelpText from "components/HelpText";
import Modal from "components/Modal"; import Modal from "components/Modal";
import Button from "components/Button"; import Button from "components/Button";

View File

@ -10,13 +10,27 @@ import Team from "models/Team";
const AUTH_STORE = "AUTH_STORE"; 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 { export default class AuthStore {
@observable user: ?User; @observable user: ?User;
@observable team: ?Team; @observable team: ?Team;
@observable token: ?string; @observable token: ?string;
@observable lastSignedIn: ?string;
@observable isSaving: boolean = false; @observable isSaving: boolean = false;
@observable isSuspended: boolean = false; @observable isSuspended: boolean = false;
@observable suspendedContactEmail: ?string; @observable suspendedContactEmail: ?string;
@observable config: ?Config;
rootStore: RootStore; rootStore: RootStore;
constructor(rootStore: RootStore) { constructor(rootStore: RootStore) {
@ -32,8 +46,12 @@ export default class AuthStore {
this.user = new User(data.user); this.user = new User(data.user);
this.team = new Team(data.team); this.team = new Team(data.team);
this.token = getCookie("accessToken"); this.token = getCookie("accessToken");
this.lastSignedIn = getCookie("lastSignedIn");
setImmediate(() => this.fetchConfig());
if (this.token) setImmediate(() => this.fetch()); if (this.token) {
setImmediate(() => this.fetch());
}
autorun(() => { autorun(() => {
try { 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 @action
fetch = async () => { fetch = async () => {
try { try {
@ -158,10 +183,16 @@ export default class AuthStore {
}) })
); );
this.token = null;
// if this logout was forced from an authenticated route then // if this logout was forced from an authenticated route then
// save the current path so we can go back there once signed in // save the current path so we can go back there once signed in
if (savePath) { if (savePath) {
setCookie("postLoginRedirectPath", window.location.pathname); const pathName = window.location.pathname;
if (pathName !== "/" && pathName !== "/create") {
setCookie("postLoginRedirectPath", pathName);
}
} }
// remove authentication token itself // remove authentication token itself
@ -178,8 +209,5 @@ export default class AuthStore {
}); });
this.team = null; 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 body;
let modifiedPath; let modifiedPath;
let urlToFetch;
if (method === "GET") { if (method === "GET") {
if (data) { if (data) {
@ -45,6 +46,12 @@ class ApiClient {
body = data ? JSON.stringify(data) : undefined; body = data ? JSON.stringify(data) : undefined;
} }
if (path.match(/^http/)) {
urlToFetch = modifiedPath || path;
} else {
urlToFetch = this.baseUrl + (modifiedPath || path);
}
// Construct headers // Construct headers
const headers = new Headers({ const headers = new Headers({
Accept: "application/json", Accept: "application/json",
@ -60,7 +67,7 @@ class ApiClient {
let response; let response;
try { try {
response = await fetch(this.baseUrl + (modifiedPath || path), { response = await fetch(urlToFetch, {
method, method,
body, body,
headers, 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 // @flow
require('./init'); require("./init");
if ( if (
!process.env.SECRET_KEY || !process.env.SECRET_KEY ||
process.env.SECRET_KEY === 'generate_a_new_key' process.env.SECRET_KEY === "generate_a_new_key"
) { ) {
console.error( 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 // $FlowFixMe
process.exit(1); process.exit(1);
@ -14,11 +14,11 @@ if (
if (process.env.AWS_ACCESS_KEY_ID) { if (process.env.AWS_ACCESS_KEY_ID) {
[ [
'AWS_REGION', "AWS_REGION",
'AWS_SECRET_ACCESS_KEY', "AWS_SECRET_ACCESS_KEY",
'AWS_S3_UPLOAD_BUCKET_URL', "AWS_S3_UPLOAD_BUCKET_URL",
'AWS_S3_UPLOAD_BUCKET_NAME', "AWS_S3_UPLOAD_BUCKET_NAME",
'AWS_S3_UPLOAD_MAX_SIZE', "AWS_S3_UPLOAD_MAX_SIZE",
].forEach(key => { ].forEach(key => {
if (!process.env[key]) { if (!process.env[key]) {
console.error(`The ${key} env variable must be set when using AWS`); 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) { if (!process.env.URL) {
console.error( 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 // $FlowFixMe
process.exit(1); process.exit(1);
@ -48,7 +48,7 @@ if (!process.env.URL) {
if (!process.env.DATABASE_URL) { if (!process.env.DATABASE_URL) {
console.error( 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 // $FlowFixMe
process.exit(1); process.exit(1);
@ -56,7 +56,7 @@ if (!process.env.DATABASE_URL) {
if (!process.env.REDIS_URL) { if (!process.env.REDIS_URL) {
console.error( 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 // $FlowFixMe
process.exit(1); process.exit(1);
@ -64,17 +64,17 @@ if (!process.env.REDIS_URL) {
if (!process.env.WEBSOCKETS_ENABLED) { if (!process.env.WEBSOCKETS_ENABLED) {
console.log( 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') { if (process.env.NODE_ENV === "production") {
console.log('\n\x1b[33m%s\x1b[0m', 'Running Outline in production mode.'); console.log("\n\x1b[33m%s\x1b[0m", "Running Outline in production mode.");
} else if (process.env.NODE_ENV === 'development') { } else if (process.env.NODE_ENV === "development") {
console.log( 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"' '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 // @flow
require('babel-core/register'); require("babel-core/register");
require('babel-polyfill'); require("babel-polyfill");
require('dotenv').config({ silent: true }); require("dotenv").config({ silent: true });

View File

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

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