Merge branch 'master' of github.com:jorilallo/atlas into collection-home
This commit is contained in:
@@ -26,24 +26,30 @@ router.post('hooks.slack', async ctx => {
|
||||
limit: 5,
|
||||
});
|
||||
|
||||
const results = [];
|
||||
let number = 1;
|
||||
for (const document of documents) {
|
||||
results.push(
|
||||
`${number}. <${process.env.URL}${document.getUrl()}|${document.title}>`
|
||||
);
|
||||
number += 1;
|
||||
}
|
||||
if (documents) {
|
||||
const results = [];
|
||||
let number = 1;
|
||||
for (const document of documents) {
|
||||
results.push(
|
||||
`${number}. <${process.env.URL}${document.getUrl()}|${document.title}>`
|
||||
);
|
||||
number += 1;
|
||||
}
|
||||
|
||||
ctx.body = {
|
||||
text: 'Search results:',
|
||||
attachments: [
|
||||
{
|
||||
text: results.join('\n'),
|
||||
color: '#3AA3E3',
|
||||
},
|
||||
],
|
||||
};
|
||||
ctx.body = {
|
||||
text: 'Search results:',
|
||||
attachments: [
|
||||
{
|
||||
text: results.join('\n'),
|
||||
color: '#3AA3E3',
|
||||
},
|
||||
],
|
||||
};
|
||||
} else {
|
||||
ctx.body = {
|
||||
text: 'No search results',
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
559
server/pages/Api.js
Normal file
559
server/pages/Api.js
Normal file
@@ -0,0 +1,559 @@
|
||||
// @flow
|
||||
import React from 'react';
|
||||
import Grid from 'styled-components-grid';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Container = styled.div`
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
|
||||
pre {
|
||||
padding: 0.5em 1em;
|
||||
background: #f9fbfc;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e8ebed;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
|
||||
thead {
|
||||
td {
|
||||
padding: 5px 12px 5px 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
vertical-align: bottom;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
tbody,
|
||||
thead {
|
||||
td {
|
||||
padding: 5px 12px 5px 0;
|
||||
}
|
||||
|
||||
td:last-child {
|
||||
width: 100%;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
code {
|
||||
font-size: 1.08em;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Pricing() {
|
||||
return (
|
||||
<Grid>
|
||||
<Helmet>
|
||||
<title>Developer API - Outline</title>
|
||||
</Helmet>
|
||||
<Container>
|
||||
<h1>Outline API</h1>
|
||||
<p>
|
||||
First thing we build for Outline was its API. It’s the heart and soul
|
||||
of the service and as developers, it’s our mission to make the API as
|
||||
rich and easy to use as possible.
|
||||
</p>
|
||||
<p>
|
||||
<i>
|
||||
While Outline is still in public beta, we might make small
|
||||
adjustments, including breaking changes to the API.
|
||||
</i>
|
||||
</p>
|
||||
<h2>Making requests</h2>
|
||||
<p>
|
||||
Outline’s API follows simple RPC style conventions where each API
|
||||
endpoint is a method on{' '}
|
||||
<code>https://www.getoutline.com/api/<METHOD></code>. Both{' '}
|
||||
<code>GET</code> and <code>POST</code> methods are supported but it’s
|
||||
recommeded that you make all call using <code>POST</code>. Only HTTPS
|
||||
is supported in production.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For <code>GET</code> requests query string parameters are expected
|
||||
(e.g.
|
||||
<code>/api/document.info?id=...&token=...</code>). When making{' '}
|
||||
<code>POST</code> requests, request parameters are parsed depending on{' '}
|
||||
<code>Content-Type</code> header. To make a call using JSON payload,
|
||||
one must pass <code>Content-Type: application/json</code> header:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Example POST request:</strong>
|
||||
</p>
|
||||
<pre>
|
||||
<code>
|
||||
{`curl https://www.getoutline.com/api/documents.info
|
||||
-X POST
|
||||
-H 'authorization: Bearer API_KEY'
|
||||
-H 'content-type: application/json'
|
||||
-H 'accept: application/json'
|
||||
-d '{"id": "outline-api-NTpezNwhUP"}'
|
||||
`}
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
<strong>Example GET request:</strong>
|
||||
</p>
|
||||
<pre>
|
||||
<code>
|
||||
{`curl https://www.getoutline.com/api/documents.info?id=outline-api-NTpezNwhUP&token=API_KEY
|
||||
`}
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<h2>Authentication</h2>
|
||||
|
||||
<p>
|
||||
To access private API endpoints, you must provide a valid API key. You
|
||||
can create new API keys in your{' '}
|
||||
<a href={`${process.env.URL}/settings`}>account settings</a>. Be
|
||||
careful when handling your keys as they give access to all of your
|
||||
documents.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To authenticate with Outline API, you can supply the API key as a
|
||||
header (<code>Authorization: Bearer YOUR_API_KEY</code>) or as part of
|
||||
the payload using <code>token</code> parameter. If you're making{' '}
|
||||
<code>GET</code> requests, header based authentication is recommended
|
||||
so that your keys don't leak into logs.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Some API endpoints allow unauhenticated requests for public resources
|
||||
and they can be called without an API key.
|
||||
</p>
|
||||
|
||||
<h2>Errors</h2>
|
||||
|
||||
<p>
|
||||
All successful API requests will be returned with <code>200</code>{' '}
|
||||
status code and <code>ok: true</code> in the response payload. If
|
||||
there’s an error while making the request, appropriate status code is
|
||||
returned with the <code>error</code> message:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
<code>
|
||||
{`{
|
||||
"ok": false,
|
||||
"error: "Not Found"
|
||||
}
|
||||
`}
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<h2>Methods</h2>
|
||||
<Methods>
|
||||
<Method method="user.info" label="Get current user">
|
||||
<Description>
|
||||
This method returns the information for currently logged in user.
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument id="id" description="Collection id" required />
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method method="user.s3Upload" label="Get S3 upload credentials">
|
||||
<Description>
|
||||
You can upload small files and images as part of your documents.
|
||||
All files are stored using Amazon S3. Instead of uploading files
|
||||
to Outline, you need to upload them directly to S3 with special
|
||||
credentials which can be obtained through this endpoint.
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument
|
||||
id="filename"
|
||||
description="Filename of the uploaded file"
|
||||
required
|
||||
/>
|
||||
<Argument
|
||||
id="kind"
|
||||
description="Mimetype of the document"
|
||||
required
|
||||
/>
|
||||
<Argument
|
||||
id="size"
|
||||
description="Filesize of the document"
|
||||
required
|
||||
/>
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method
|
||||
method="collections.list"
|
||||
label="List your document collections"
|
||||
>
|
||||
<Description>List all your document collections.</Description>
|
||||
<Arguments pagination />
|
||||
</Method>
|
||||
|
||||
<Method method="collections.info" label="Get a document collection">
|
||||
<Description>
|
||||
Returns detailed information on a document collection.
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument id="id" description="Collection id" required />
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method
|
||||
method="collections.create"
|
||||
label="Create a document collection"
|
||||
>
|
||||
<Description>Creates a new document collection.</Description>
|
||||
<Arguments>
|
||||
<Argument id="name" description="Collection name" required />
|
||||
<Argument
|
||||
id="description"
|
||||
description="Short description for the collection"
|
||||
/>
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method method="collections.update" label="Update a collection">
|
||||
<Description>
|
||||
This method allows you to modify already created document.
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument id="id" description="Collection ID" required />
|
||||
<Argument id="name" description="Name for the collection" />
|
||||
<Argument
|
||||
id="color"
|
||||
description="Collection color in hex form (e.g. #E1E1E1)"
|
||||
/>
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method method="collections.delete" label="Delete a collection">
|
||||
<Description>
|
||||
Delete a collection and all of its documents. This action can`t be
|
||||
undone so please be careful.
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument id="id" description="Collection ID" required />
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method method="documents.info" label="Get a document">
|
||||
<Description>
|
||||
<p>
|
||||
This method returns information for a document with a specific
|
||||
ID. Following identifiers are allowed:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
UUID - <code>id</code> field of the document
|
||||
</li>
|
||||
<li>
|
||||
URI identifier - Human readable identifier used in Outline
|
||||
URLs (e.g. <code>outline-api-i48ZEZc5zjXndcP</code>)
|
||||
</li>
|
||||
</ul>
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument
|
||||
id="id"
|
||||
description="Document id or URI identifier"
|
||||
required
|
||||
/>
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method method="documents.search" label="Search documents">
|
||||
<Description>
|
||||
This methods allows you to search all of your documents with
|
||||
keywords.
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument id="query" description="Search query" required />
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method method="documents.create" label="Create a new document">
|
||||
<Description>
|
||||
This method allows you to publish a new document under an existing
|
||||
collection. By default a document is set to the parent collection
|
||||
root. If you want to create a subdocument, you can pass{' '}
|
||||
<code>parentDocument</code> to set parent document.
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument
|
||||
id="collection"
|
||||
description={
|
||||
<span>
|
||||
<code>ID</code> of the collection to which the document is
|
||||
created
|
||||
</span>
|
||||
}
|
||||
required
|
||||
/>
|
||||
<Argument
|
||||
id="title"
|
||||
description="Title for the document"
|
||||
required
|
||||
/>
|
||||
<Argument
|
||||
id="text"
|
||||
description="Content of the document in Markdow"
|
||||
required
|
||||
/>
|
||||
<Argument
|
||||
id="parentDocument"
|
||||
description={
|
||||
<span>
|
||||
<code>ID</code> of the parent document within the collection
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method method="documents.update" label="Update a document">
|
||||
<Description>
|
||||
This method allows you to modify already created document.
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument
|
||||
id="id"
|
||||
description="Document id or URI identifier"
|
||||
required
|
||||
/>
|
||||
<Argument id="title" description="Title for the document" />
|
||||
<Argument
|
||||
id="text"
|
||||
description="Content of the document in Markdown"
|
||||
/>
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method method="documents.move" label="Move document in a collection">
|
||||
<Description>
|
||||
Move a document into a new location inside the collection. This is
|
||||
easily done by defining the parent document ID and optional index.
|
||||
If no parent document is provided, the document will be moved to
|
||||
the collection root.
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument
|
||||
id="id"
|
||||
description="Document id or URI identifier"
|
||||
required
|
||||
/>
|
||||
<Argument
|
||||
id="parentDocument"
|
||||
description="ID of the new parent document (if any)"
|
||||
/>
|
||||
<Argument id="index" description="Index of the new location" />
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method method="documents.delete" label="Delete a document">
|
||||
<Description>
|
||||
Delete a document and all of its child documents if any.
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument
|
||||
id="id"
|
||||
description="Document id or URI identifier"
|
||||
required
|
||||
/>
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method method="documents.info" label="Get a document">
|
||||
<Description>
|
||||
Get a document with its ID or URL identifier from user’s
|
||||
collections.
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument
|
||||
id="id"
|
||||
description="Document id or URI identifier"
|
||||
required
|
||||
/>
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method method="documents.star" label="Star a document">
|
||||
<Description>
|
||||
Star (favorite) a document for authenticated user.
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument
|
||||
id="id"
|
||||
description="Document id or URI identifier"
|
||||
required
|
||||
/>
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method method="documents.unstar" label="Unstar a document">
|
||||
<Description>
|
||||
Unstar as starred (favorited) a document for authenticated user.
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument
|
||||
id="id"
|
||||
description="Document id or URI identifier"
|
||||
required
|
||||
/>
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method
|
||||
method="documents.viewed"
|
||||
label="Get recently viewed document for user"
|
||||
>
|
||||
<Description>
|
||||
Return recently viewed documents for the authenticated user
|
||||
</Description>
|
||||
<Arguments pagination />
|
||||
</Method>
|
||||
|
||||
<Method
|
||||
method="documents.starred"
|
||||
label="Get recently starred document for user"
|
||||
>
|
||||
<Description>
|
||||
Return recently starred documents for the authenticated user
|
||||
</Description>
|
||||
<Arguments pagination />
|
||||
</Method>
|
||||
|
||||
<Method
|
||||
method="documents.revisions"
|
||||
label="Get revisions for a document"
|
||||
>
|
||||
<Description>
|
||||
Return revisions for a document. Upon each edit, a new revision is
|
||||
stored.
|
||||
</Description>
|
||||
<Arguments pagination />
|
||||
</Method>
|
||||
</Methods>
|
||||
</Container>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
const MethodList = styled.ul`
|
||||
margin-bottom: 80px;
|
||||
`;
|
||||
|
||||
const Methods = (props: { children: React.Element<*> }) => {
|
||||
const children = React.Children.toArray(props.children);
|
||||
const methods = children.map(child => child.props.method);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<MethodList>
|
||||
{methods.map(method => (
|
||||
<li key={method}>
|
||||
<a href={`#${method}`}>{method}</a>
|
||||
</li>
|
||||
))}
|
||||
</MethodList>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const MethodContainer = styled.div`
|
||||
margin-bottom: 80px;
|
||||
`;
|
||||
|
||||
const Request = styled.h4`
|
||||
text-transform: capitalize;
|
||||
`;
|
||||
|
||||
type MethodProps = {
|
||||
method: string,
|
||||
label: string,
|
||||
children: React.Element<*>,
|
||||
};
|
||||
|
||||
const Method = (props: MethodProps) => {
|
||||
const children = React.Children.toArray(props.children);
|
||||
const description = children.find(child => child.type === Description);
|
||||
const apiArgs = children.find(child => child.type === Arguments);
|
||||
|
||||
return (
|
||||
<MethodContainer>
|
||||
<h3 id={props.method}>
|
||||
<code>{props.method}</code> - {props.label}
|
||||
</h3>
|
||||
<div>{description}</div>
|
||||
<Request>HTTP request & arguments</Request>
|
||||
<p>
|
||||
<code>{`${process.env.URL}/api/${props.method}`}</code>
|
||||
</p>
|
||||
{apiArgs}
|
||||
</MethodContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const Description = (props: { children: React.Element<*> }) => (
|
||||
<p>{props.children}</p>
|
||||
);
|
||||
|
||||
type ArgumentsProps = {
|
||||
pagination?: boolean,
|
||||
children?: React.Element<*> | string,
|
||||
};
|
||||
|
||||
const Arguments = (props: ArgumentsProps) => (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Argument</td>
|
||||
<td>Required</td>
|
||||
<td>Description</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<Argument id="token" description="Authentication token" required />
|
||||
{props.pagination && (
|
||||
// $FlowIssue
|
||||
<PaginationArguments />
|
||||
)}
|
||||
{props.children}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
type ArgumentProps = {
|
||||
id: string,
|
||||
required?: boolean,
|
||||
description: React.Element<*> | string,
|
||||
};
|
||||
|
||||
const Argument = (props: ArgumentProps) => (
|
||||
<tr>
|
||||
<td>
|
||||
<code>{props.id}</code>
|
||||
</td>
|
||||
<td>
|
||||
<i>{props.required ? 'required' : 'optional'}</i>
|
||||
</td>
|
||||
<td>{props.description}</td>
|
||||
</tr>
|
||||
);
|
||||
const PaginationArguments = () => [
|
||||
<Argument id="offset" description="Pagination offset" />,
|
||||
<Argument id="limit" description="Pagination limit" />,
|
||||
];
|
||||
@@ -14,8 +14,8 @@ function Home() {
|
||||
<Hero>
|
||||
<h1>Your team’s knowledge base</h1>
|
||||
<HeroText>
|
||||
Documentation, meeting notes, playbooks, onboarding, work logs,
|
||||
brainstorming, decisions, & more…
|
||||
Team wiki, documentation, meeting notes, playbooks, onboarding, work
|
||||
logs, brainstorming, & more…
|
||||
</HeroText>
|
||||
<p>
|
||||
<SignupButton />
|
||||
@@ -24,7 +24,7 @@ function Home() {
|
||||
<Features>
|
||||
<Grid.Unit size={{ desktop: 1 / 3, tablet: 1 / 2 }}>
|
||||
<Feature>
|
||||
<h2>Blazing Fast</h2>
|
||||
<h2>Blazing Fast Wiki</h2>
|
||||
<p>
|
||||
Outline is fast, really fast. We’ve worked hard to ensure
|
||||
millisecond response times, documents load instantly, search is
|
||||
@@ -34,9 +34,10 @@ function Home() {
|
||||
<Feature>
|
||||
<h2># Markdown Support</h2>
|
||||
<p>
|
||||
Outline stores all documents in plain Markdown. Shortcuts are
|
||||
also built right into the editor so you can easily format using{' '}
|
||||
<strong>**markdown syntax**</strong> if you like.
|
||||
Outline stores, imports and exports all documents in plain
|
||||
Markdown. Shortcuts are also built right into the editor so you
|
||||
can easily format using <strong>**markdown syntax**</strong> if
|
||||
you like.
|
||||
</p>
|
||||
</Feature>
|
||||
</Grid.Unit>
|
||||
@@ -85,9 +86,9 @@ function Home() {
|
||||
<p>
|
||||
On the same page as us? Create a beta account to give Outline a try.
|
||||
</p>
|
||||
<p>
|
||||
<FooterCTA>
|
||||
<SignupButton />
|
||||
</p>
|
||||
</FooterCTA>
|
||||
</Footer>
|
||||
</Grid>
|
||||
</span>
|
||||
@@ -134,6 +135,10 @@ const Footer = styled.div`
|
||||
padding: 6em;
|
||||
`;
|
||||
|
||||
const FooterCTA = styled.p`
|
||||
padding-top: 1em;
|
||||
`;
|
||||
|
||||
const HeroText = styled.p`
|
||||
font-size: 18px;
|
||||
max-width: 600px;
|
||||
|
||||
@@ -4,6 +4,7 @@ import styled from 'styled-components';
|
||||
const Hero = styled.div`
|
||||
width: 100%;
|
||||
height: 70vh;
|
||||
max-height: 600px;
|
||||
padding: 6em 2em 0;
|
||||
text-align: center;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Navigation from './Navigation';
|
||||
import { TopNavigation, BottomNavigation } from './Navigation';
|
||||
import Analytics from '../../../shared/components/Analytics';
|
||||
import globalStyles from '../../../shared/styles/globals';
|
||||
import { color } from '../../../shared/styles/constants';
|
||||
@@ -48,8 +48,9 @@ export default function Layout({ children }: Props) {
|
||||
{'{{CSS}}'}
|
||||
</head>
|
||||
<body>
|
||||
<Navigation />
|
||||
<TopNavigation />
|
||||
{children}
|
||||
<BottomNavigation />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
// @flow
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { signin, developers, blogUrl } from '../../utils/routeHelpers';
|
||||
import {
|
||||
signin,
|
||||
developers,
|
||||
githubUrl,
|
||||
spectrumUrl,
|
||||
blogUrl,
|
||||
twitterUrl,
|
||||
} from '../../utils/routeHelpers';
|
||||
import { color } from '../../../shared/styles/constants';
|
||||
|
||||
function Navigation() {
|
||||
function TopNavigation() {
|
||||
return (
|
||||
<Nav>
|
||||
<Brand href="/">Outline</Brand>
|
||||
@@ -26,12 +33,38 @@ function Navigation() {
|
||||
);
|
||||
}
|
||||
|
||||
function BottomNavigation() {
|
||||
return (
|
||||
<BottomNav>
|
||||
<Menu>
|
||||
<MenuItem>
|
||||
<a href={githubUrl()}>GitHub</a>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<a href={spectrumUrl()}>Spectrum</a>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<a href={blogUrl()}>Medium</a>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<a href={twitterUrl()}>Twitter</a>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</BottomNav>
|
||||
);
|
||||
}
|
||||
|
||||
const Nav = styled.nav`
|
||||
display: flex;
|
||||
padding: 20px 30px;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const BottomNav = styled(Nav)`
|
||||
margin-bottom: 30px;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const Menu = styled.ul`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -52,6 +85,10 @@ const MenuItem = styled.li`
|
||||
color: ${color.slateDark};
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const Brand = styled.a`
|
||||
@@ -61,4 +98,4 @@ const Brand = styled.a`
|
||||
color: ${color.black};
|
||||
`;
|
||||
|
||||
export default Navigation;
|
||||
export { TopNavigation, BottomNavigation };
|
||||
|
||||
@@ -8,10 +8,12 @@ import sendfile from 'koa-sendfile';
|
||||
import serve from 'koa-static';
|
||||
import subdomainRedirect from './middlewares/subdomainRedirect';
|
||||
import renderpage from './utils/renderpage';
|
||||
import { slackAuth } from '../shared/utils/routeHelpers';
|
||||
|
||||
import Home from './pages/Home';
|
||||
import About from './pages/About';
|
||||
import Pricing from './pages/Pricing';
|
||||
import Api from './pages/Api';
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
const koa = new Koa();
|
||||
@@ -43,9 +45,23 @@ if (process.env.NODE_ENV === 'production') {
|
||||
});
|
||||
}
|
||||
|
||||
// slack direct install
|
||||
router.get('/auth/slack/install', async ctx => {
|
||||
const state = Math.random()
|
||||
.toString(36)
|
||||
.substring(7);
|
||||
|
||||
ctx.cookies.set('state', state, {
|
||||
httpOnly: false,
|
||||
expires: new Date('2100'),
|
||||
});
|
||||
ctx.redirect(slackAuth(state));
|
||||
});
|
||||
|
||||
// static pages
|
||||
router.get('/about', ctx => renderpage(ctx, <About />));
|
||||
router.get('/pricing', ctx => renderpage(ctx, <Pricing />));
|
||||
router.get('/developers', ctx => renderpage(ctx, <Api />));
|
||||
|
||||
// home page
|
||||
router.get('/', async ctx => {
|
||||
|
||||
@@ -8,6 +8,14 @@ export function blogUrl(): string {
|
||||
return 'https://medium.com/getoutline';
|
||||
}
|
||||
|
||||
export function twitterUrl(): string {
|
||||
return 'https://twitter.com/getoutline';
|
||||
}
|
||||
|
||||
export function spectrumUrl(): string {
|
||||
return 'https://spectrum.chat/outline';
|
||||
}
|
||||
|
||||
export function developers(): string {
|
||||
return '/developers';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user