Skip to content

Commit 62c5262

Browse files
authored
Standalone Private Sandbox/Container Auth (codesandbox#2914)
* Add private sandbox support * Allow for standalone auth * Add error reset * Fix types * Make privacy an explicit property to ensure updates * Update * Simplify the yarn.lock * Simplify yarn.lock more * Fix typing issue * Update fallback
1 parent 2aead00 commit 62c5262

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1554
-518
lines changed

.yarnclean

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ docs
99
doc
1010
website
1111
images
12-
assets
1312

1413
# examples
1514
example

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"test:ci": "lerna run test --ignore codesandbox-browserfs -- --ci --testResultsProcessor=\"jest-junit\" ",
6161
"test:integrations": "lerna exec --scope app --stream -- yarn test:integrations",
6262
"test:jest-lite": "lerna exec --scope app --stream -- yarn run test jest-lite --watch --coverage",
63-
"typecheck": "lerna run typecheck --scope app"
63+
"typecheck": "lerna run typecheck"
6464
},
6565
"dependencies": {
6666
"@typescript-eslint/eslint-plugin": "^1.13.0",

packages/app/scripts/start.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ function addMiddleware(devServer, index) {
229229
devServer.use(
230230
'/api',
231231
proxy({
232-
target: 'https://codesandbox.io',
232+
target: 'https://codesandbox.stream',
233233
changeOrigin: true,
234234
})
235235
);

packages/app/src/app/overmind/actions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export const searchMounted: AsyncAction = withLoadApp();
1919

2020
export const codesadboxMounted: AsyncAction = withLoadApp();
2121

22+
export const genericPageMounted: AsyncAction = withLoadApp();
23+
2224
export const cliMounted: AsyncAction = withLoadApp(
2325
async ({ state, actions }) => {
2426
if (state.user) {

packages/app/src/app/overmind/effects/api/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,10 @@ export default {
411411
},
412412
});
413413
},
414-
updatePrivacy(sandboxId: string, privacy: 0 | 1 | 2): Promise<void> {
414+
updatePrivacy(
415+
sandboxId: string,
416+
privacy: 0 | 1 | 2
417+
): Promise<SandboxAPIResponse> {
415418
return api.patch(`/sandboxes/${sandboxId}/privacy`, {
416419
sandbox: {
417420
privacy,

packages/app/src/app/overmind/namespaces/workspace/actions.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -180,19 +180,22 @@ export const sandboxDeleted: AsyncAction = async ({
180180
export const sandboxPrivacyChanged: AsyncAction<{
181181
privacy: 0 | 1 | 2;
182182
}> = async ({ state, effects, actions }, { privacy }) => {
183+
const oldPrivacy = state.editor.currentSandbox.privacy;
184+
const sandbox = await effects.api.updatePrivacy(
185+
state.editor.currentId,
186+
privacy
187+
);
188+
state.editor.currentSandbox.privacy = privacy;
189+
state.editor.currentSandbox.previewSecret = sandbox.previewSecret;
190+
183191
if (
184192
getTemplate(state.editor.currentSandbox.template).isServer &&
185-
privacy === 2
193+
((oldPrivacy !== 2 && privacy === 2) || (oldPrivacy === 2 && privacy !== 2))
186194
) {
187-
actions.modalOpened({
188-
modal: 'privacyServerWarning',
189-
message: null,
190-
});
195+
// Privacy changed from private to unlisted/public or other way around, restart
196+
// the sandbox to notify containers
197+
actions.server.restartContainer();
191198
}
192-
193-
await effects.api.updatePrivacy(state.editor.currentId, privacy);
194-
195-
state.editor.currentSandbox.privacy = privacy;
196199
};
197200

198201
export const setWorkspaceItem: Action<{

packages/app/src/app/pages/Dashboard/elements.ts

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import styled, { css } from 'styled-components';
2-
import Logo from '@codesandbox/common/lib/components/Logo';
32

43
export const Container = styled.div`
54
height: 100%;
@@ -8,64 +7,6 @@ export const Container = styled.div`
87
color: rgba(255, 255, 255, 0.8);
98
`;
109

11-
export const Centered = styled.div`
12-
display: flex;
13-
height: 100vh;
14-
width: 100vw;
15-
align-items: center;
16-
justify-content: center;
17-
`;
18-
19-
export const OffsettedLogo = styled(Logo)`
20-
margin-top: -110px;
21-
margin-bottom: 30px;
22-
color: white;
23-
24-
background-color: ${props => props.theme.background4};
25-
width: 75px;
26-
height: 75px;
27-
28-
padding: 1rem;
29-
border-radius: 8px;
30-
`;
31-
32-
export const LoggedInContainer = styled.div`
33-
display: flex;
34-
justify-content: center;
35-
align-items: center;
36-
flex-direction: column;
37-
38-
width: 300px;
39-
margin: 0 auto;
40-
41-
padding: 4rem;
42-
border-radius: 4px;
43-
background-color: ${props => props.theme.background};
44-
`;
45-
46-
export const LoggedInTitle = styled.div`
47-
font-size: 1.5rem;
48-
color: ${props => props.theme.new.title};
49-
font-weight: 600;
50-
margin-bottom: 2rem;
51-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
52-
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
53-
`;
54-
55-
export const LoggedInSubTitle = styled.div`
56-
font-weight: 500;
57-
font-size: 1.5rem;
58-
margin-bottom: 2rem;
59-
`;
60-
61-
export const NavigationContainer = styled.div`
62-
position: absolute;
63-
left: 0;
64-
top: 0;
65-
right: 0;
66-
padding: 1rem;
67-
`;
68-
6910
export const ShowSidebarButton = styled.button`
7011
display: none;
7112
transition: opacity 200ms ease;

packages/app/src/app/pages/Dashboard/index.tsx

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,18 @@ import React, { useState, useEffect } from 'react';
22
import { useOvermind } from 'app/overmind';
33
import RightIcon from 'react-icons/lib/md/keyboard-arrow-right';
44
import LeftIcon from 'react-icons/lib/md/keyboard-arrow-left';
5-
import { withRouter } from 'react-router-dom';
5+
import { withRouter, Redirect } from 'react-router-dom';
66
import { Navigation } from 'app/pages/common/Navigation';
7-
import { SignInButton } from 'app/pages/common/SignInButton';
7+
import { signInPageUrl } from '@codesandbox/common/lib/utils/url-generator';
88
import { client } from 'app/graphql/client';
99

1010
import SidebarContents from './Sidebar';
1111
import Content from './Content';
1212
import {
1313
Container,
14-
Centered,
1514
Content as ContentContainer,
16-
LoggedInContainer,
17-
LoggedInTitle,
1815
Sidebar,
19-
NavigationContainer,
2016
ShowSidebarButton,
21-
OffsettedLogo,
2217
} from './elements';
2318

2419
const Dashboard = props => {
@@ -55,7 +50,7 @@ const Dashboard = props => {
5550
onRouteChange();
5651
});
5752

58-
let DashboardContent = (
53+
const DashboardContent = (
5954
<>
6055
<Sidebar active={showSidebar}>
6156
<SidebarContents />
@@ -75,25 +70,12 @@ const Dashboard = props => {
7570
);
7671

7772
if (!hasLogIn) {
78-
DashboardContent = (
79-
<Container>
80-
<Centered>
81-
<LoggedInContainer>
82-
<OffsettedLogo />
83-
<LoggedInTitle>Sign in to CodeSandbox</LoggedInTitle>
84-
85-
<SignInButton big style={{ fontSize: '1rem' }} />
86-
</LoggedInContainer>
87-
</Centered>
88-
</Container>
89-
);
73+
return <Redirect to={signInPageUrl()} />;
9074
}
9175

9276
return (
9377
<Container>
94-
<NavigationContainer>
95-
<Navigation searchNoInput title="Dashboard" />
96-
</NavigationContainer>
78+
<Navigation float searchNoInput title="Dashboard" />
9779

9880
<div style={{ display: 'flex', overflow: 'hidden' }}>
9981
{DashboardContent}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import React, { useEffect } from 'react';
2+
import { useOvermind } from 'app/overmind';
3+
import { Redirect, RouteComponentProps, withRouter } from 'react-router-dom';
4+
import Centered from '@codesandbox/common/lib/components/flex/Centered';
5+
import Fullscreen from '@codesandbox/common/lib/components/flex/Fullscreen';
6+
7+
import {
8+
signInPageUrl,
9+
frameUrl,
10+
} from '@codesandbox/common/lib/utils/url-generator';
11+
12+
import { Title } from 'app/components/Title';
13+
14+
// This route is supposed to be opened in a new window.
15+
// It is called from a sandbox so that we can try to retrieve
16+
// a sandbox from the root domain and return the preview secret.
17+
// This is purely used to auth a sandbox. It should return a postMessage
18+
// with the previewSecret to the parent
19+
const PreviewAuth = (props: RouteComponentProps<{ id: string }>) => {
20+
const {
21+
state,
22+
actions: { genericPageMounted },
23+
effects,
24+
} = useOvermind();
25+
26+
const [error, setError] = React.useState<string>();
27+
28+
useEffect(() => {
29+
genericPageMounted();
30+
}, [genericPageMounted]);
31+
32+
useEffect(() => {
33+
if (state.hasLogIn) {
34+
setError(null);
35+
// eslint-disable-next-line
36+
const [id, port] = props.match.params.id.split('-');
37+
38+
effects.api
39+
.getSandbox(id)
40+
.then(sandbox => {
41+
const sandboxUrl = frameUrl(sandbox, '', {
42+
port: port ? Number.parseInt(port, 10) : undefined,
43+
});
44+
// Only send domains to urls from this sandbox
45+
const trustedDomain = new URL(sandboxUrl).origin;
46+
47+
const listener = (e: MessageEvent) => {
48+
if (e.data && e.data.$type === 'request-preview-secret') {
49+
(e.source as WindowProxy).postMessage(
50+
{
51+
$type: 'preview-secret',
52+
previewSecret: sandbox.previewSecret,
53+
},
54+
trustedDomain
55+
);
56+
57+
window.removeEventListener('message', listener);
58+
}
59+
};
60+
61+
window.addEventListener('message', listener);
62+
})
63+
.catch(e => {
64+
setError("We couldn't find the sandbox");
65+
});
66+
}
67+
}, [effects.api, props.match.params.id, state.hasLogIn]);
68+
69+
return state.hasLogIn ? (
70+
<Fullscreen style={{ height: '100vh' }}>
71+
<Centered horizontal vertical>
72+
<Title>{error ? 'Error: ' + error : 'Fetching Sandbox'}</Title>
73+
</Centered>
74+
</Fullscreen>
75+
) : (
76+
<Redirect to={signInPageUrl(location.pathname)} />
77+
);
78+
};
79+
80+
// eslint-disable-next-line import/no-default-export
81+
export default withRouter(PreviewAuth);

packages/app/src/app/pages/Sandbox/Editor/Content/Preview/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ class PreviewComponent extends Component<Props, State> {
180180
<BasePreview
181181
onInitialized={this.onPreviewInitialized}
182182
sandbox={store.editor.currentSandbox}
183+
privacy={store.editor.currentSandbox.privacy}
184+
previewSecret={store.editor.currentSandbox.previewSecret}
183185
currentModule={store.editor.currentModule}
184186
settings={store.preferences.settings}
185187
initialPath={store.editor.initialPath}

0 commit comments

Comments
 (0)