Skip to content

Commit db25e59

Browse files
VitGottwaldCompuIves
authored andcommitted
Fix local development build
By using codesandbox.io instead of codesandbox.stream
1 parent df2e909 commit db25e59

File tree

1 file changed

+212
-0
lines changed

1 file changed

+212
-0
lines changed
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
import io from 'socket.io-client';
2+
import { dispatch } from 'codesandbox-api';
3+
import _debug from 'debug';
4+
5+
import { IExecutor, IFiles, ISetupParams } from './executor';
6+
7+
const debug = _debug('executors:server');
8+
9+
function sseTerminalMessage(msg: string) {
10+
dispatch({
11+
type: 'terminal:message',
12+
data: `> Sandbox Container: ${msg}\n\r`,
13+
});
14+
}
15+
16+
/**
17+
* Find the changes from the last run, we only work with saved code here.
18+
*/
19+
const getDiff = (oldFiles: IFiles, newFiles: IFiles) => {
20+
const diff: IFiles = {};
21+
22+
Object.keys(newFiles)
23+
.filter(p => {
24+
const newSavedCode = newFiles[p].savedCode || newFiles[p].code;
25+
if (oldFiles[p]) {
26+
const oldSavedCode = oldFiles[p].savedCode || oldFiles[p].code;
27+
if (oldSavedCode !== newSavedCode) {
28+
return true;
29+
}
30+
} else {
31+
return true;
32+
}
33+
34+
return false;
35+
})
36+
.forEach(p => {
37+
diff[p] = {
38+
code: newFiles[p].code,
39+
path: newFiles[p].path,
40+
savedCode: newFiles[p].savedCode,
41+
isBinary: newFiles[p].isBinary,
42+
};
43+
});
44+
45+
Object.keys(oldFiles).forEach(p => {
46+
if (!newFiles[p]) {
47+
diff[p] = {
48+
path: oldFiles[p].path,
49+
isBinary: false,
50+
code: null,
51+
savedCode: null,
52+
};
53+
}
54+
});
55+
56+
return diff;
57+
};
58+
59+
const MAX_SSE_AGE = 24 * 60 * 60 * 1000; // 1 day
60+
const tick = () => new Promise(r => setTimeout(() => r(), 0));
61+
62+
export class ServerExecutor implements IExecutor {
63+
socket: SocketIOClient.Socket;
64+
connectTimeout: number | null = null;
65+
token: Promise<string | undefined>;
66+
sandboxId?: string;
67+
lastSent?: IFiles;
68+
69+
constructor() {
70+
this.socket = this.initializeSocket();
71+
this.token = this.retrieveSSEToken();
72+
}
73+
74+
private initializeSocket() {
75+
return io(`https://sse.codesandbox.io`, {
76+
autoConnect: false,
77+
transports: ['websocket', 'polling'],
78+
});
79+
}
80+
81+
async initialize({ sandboxId, files }: ISetupParams) {
82+
if (this.sandboxId === sandboxId && this.socket.connected) {
83+
return Promise.resolve();
84+
}
85+
86+
this.sandboxId = sandboxId;
87+
this.lastSent = files;
88+
89+
await this.dispose();
90+
await tick();
91+
92+
this.socket = this.initializeSocket();
93+
}
94+
95+
public async setup() {
96+
debug('Setting up server executor...');
97+
98+
return this.openSocket();
99+
}
100+
101+
public async dispose() {
102+
this.socket.removeAllListeners();
103+
this.socket.close();
104+
}
105+
106+
public updateFiles(newFiles: IFiles) {
107+
const changedFiles = this.lastSent
108+
? getDiff(this.lastSent, newFiles)
109+
: newFiles;
110+
111+
this.lastSent = newFiles;
112+
113+
if (Object.keys(changedFiles).length > 0 && this.socket) {
114+
debug(
115+
Object.keys(changedFiles).length + ' files changed, sending to SSE.'
116+
);
117+
debug(changedFiles);
118+
this.socket.emit('sandbox:update', changedFiles);
119+
}
120+
}
121+
122+
public emit(event: string, data?: any) {
123+
this.socket.emit(event, data);
124+
}
125+
126+
public on(event: string, listener: (data: any) => void) {
127+
this.socket.on(event, listener);
128+
}
129+
130+
private openSocket() {
131+
if (this.socket.connected) {
132+
return Promise.resolve();
133+
}
134+
135+
return new Promise<void>((resolve, reject) => {
136+
this.socket.on('connect', async () => {
137+
try {
138+
if (this.connectTimeout) {
139+
clearTimeout(this.connectTimeout);
140+
this.connectTimeout = null;
141+
}
142+
143+
await this.startSandbox();
144+
145+
resolve();
146+
} catch (e) {
147+
debug('Error connecting to SSE manager: ', e);
148+
reject(e);
149+
}
150+
});
151+
152+
this.socket.on('sandbox:start', () => {
153+
sseTerminalMessage(`Sandbox ${this.sandboxId} started`);
154+
});
155+
156+
this.socket.open();
157+
});
158+
}
159+
160+
private async startSandbox() {
161+
const token = await this.token;
162+
this.socket.emit('sandbox', { id: this.sandboxId, token });
163+
164+
debug('Connected to sse manager, sending start signal...');
165+
sseTerminalMessage(`Starting sandbox ${this.sandboxId}...`);
166+
this.socket.emit('sandbox:start');
167+
}
168+
169+
private async retrieveSSEToken() {
170+
debug('Retrieving SSE token...');
171+
const jwt = localStorage.getItem('jwt');
172+
173+
if (jwt) {
174+
const parsedJWT = JSON.parse(jwt);
175+
const existingKey = localStorage.getItem('sse');
176+
const currentTime = new Date().getTime();
177+
178+
if (existingKey) {
179+
const parsedKey = JSON.parse(existingKey);
180+
if (parsedKey.key && currentTime - parsedKey.timestamp < MAX_SSE_AGE) {
181+
debug('Retrieved SSE token from cache');
182+
return parsedKey.key as string;
183+
}
184+
}
185+
186+
return fetch('/api/v1/users/current_user/sse', {
187+
method: 'POST',
188+
headers: {
189+
'Content-Type': 'application/json',
190+
Authorization: `Bearer ${parsedJWT}`,
191+
},
192+
})
193+
.then(x => x.json())
194+
.then(result => result.jwt)
195+
.then((token: string) => {
196+
debug('Retrieved SSE token from API');
197+
localStorage.setItem(
198+
'sse',
199+
JSON.stringify({
200+
key: token,
201+
timestamp: currentTime,
202+
})
203+
);
204+
205+
return token;
206+
});
207+
}
208+
209+
debug('Not signed in, returning undefined');
210+
return undefined;
211+
}
212+
}

0 commit comments

Comments
 (0)