5
5
*/
6
6
7
7
import {
8
+ Emitter ,
8
9
GitpodClient ,
9
10
GitpodServer ,
10
11
GitpodServerPath ,
11
12
GitpodService ,
12
13
GitpodServiceImpl ,
14
+ User ,
15
+ WorkspaceInfo ,
13
16
} from "@gitpod/gitpod-protocol" ;
14
17
import { WebSocketConnectionProvider } from "@gitpod/gitpod-protocol/lib/messaging/browser/connection" ;
15
- import { createWindowMessageConnection } from "@gitpod/gitpod-protocol/lib/messaging/browser/window-connection" ;
16
- import { JsonRpcProxyFactory } from "@gitpod/gitpod-protocol/lib/messaging/proxy-factory" ;
17
18
import { GitpodHostUrl } from "@gitpod/gitpod-protocol/lib/util/gitpod-host-url" ;
18
19
import { log } from "@gitpod/gitpod-protocol/lib/util/logging" ;
20
+ import { IDEFrontendDashboardService } from "@gitpod/gitpod-protocol/lib/frontend-dashboard-service" ;
21
+ import { RemoteTrackMessage } from "@gitpod/gitpod-protocol/lib/analytics" ;
19
22
20
23
export const gitpodHostUrl = new GitpodHostUrl ( window . location . toString ( ) ) ;
21
24
22
25
function createGitpodService < C extends GitpodClient , S extends GitpodServer > ( ) {
23
- if ( window . top !== window . self && process . env . NODE_ENV === "production" ) {
24
- const connection = createWindowMessageConnection ( "gitpodServer" , window . parent , "*" ) ;
25
- const factory = new JsonRpcProxyFactory < S > ( ) ;
26
- const proxy = factory . createProxy ( ) ;
27
- factory . listen ( connection ) ;
28
- return new GitpodServiceImpl < C , S > ( proxy , {
29
- onReconnect : async ( ) => {
30
- await connection . sendRequest ( "$reconnectServer" ) ;
31
- } ,
32
- } ) ;
33
- }
34
26
let host = gitpodHostUrl . asWebsocket ( ) . with ( { pathname : GitpodServerPath } ) . withApi ( ) ;
35
27
36
28
const connectionProvider = new WebSocketConnectionProvider ( ) ;
@@ -53,7 +45,7 @@ function createGitpodService<C extends GitpodClient, S extends GitpodServer>() {
53
45
return new GitpodServiceImpl < C , S > ( proxy , { onReconnect } ) ;
54
46
}
55
47
56
- function getGitpodService ( ) : GitpodService {
48
+ export function getGitpodService ( ) : GitpodService {
57
49
const w = window as any ;
58
50
const _gp = w . _gp || ( w . _gp = { } ) ;
59
51
if ( window . location . search . includes ( "service=mock" ) ) {
@@ -64,4 +56,143 @@ function getGitpodService(): GitpodService {
64
56
return service ;
65
57
}
66
58
67
- export { getGitpodService } ;
59
+ let ideFrontendService : IDEFrontendService | undefined ;
60
+ export function getIDEFrontendService ( workspaceID : string , sessionId : string , service : GitpodService ) {
61
+ if ( ! ideFrontendService ) {
62
+ ideFrontendService = new IDEFrontendService ( workspaceID , sessionId , service , window . parent ) ;
63
+ }
64
+ return ideFrontendService ;
65
+ }
66
+
67
+ export class IDEFrontendService implements IDEFrontendDashboardService . IServer {
68
+ private instanceID : string | undefined ;
69
+ private ideUrl : URL | undefined ;
70
+ private user : User | undefined ;
71
+
72
+ private latestStatus ?: IDEFrontendDashboardService . Status ;
73
+
74
+ private readonly onDidChangeEmitter = new Emitter < IDEFrontendDashboardService . SetStateData > ( ) ;
75
+ readonly onSetState = this . onDidChangeEmitter . event ;
76
+
77
+ constructor (
78
+ private workspaceID : string ,
79
+ private sessionId : string ,
80
+ private service : GitpodService ,
81
+ private clientWindow : Window ,
82
+ ) {
83
+ this . processServerInfo ( ) ;
84
+ window . addEventListener ( "message" , ( event : MessageEvent ) => {
85
+ if ( event . origin !== this . ideUrl ?. origin ) {
86
+ return ;
87
+ }
88
+
89
+ if ( IDEFrontendDashboardService . isTrackEventData ( event . data ) ) {
90
+ this . trackEvent ( event . data . msg ) ;
91
+ }
92
+ if ( IDEFrontendDashboardService . isHeartbeatEventData ( event . data ) ) {
93
+ this . activeHeartbeat ( ) ;
94
+ }
95
+ if ( IDEFrontendDashboardService . isSetStateEventData ( event . data ) ) {
96
+ this . onDidChangeEmitter . fire ( event . data . state ) ;
97
+ }
98
+ } ) ;
99
+ window . addEventListener ( "unload" , ( ) => {
100
+ if ( ! this . instanceID ) {
101
+ return ;
102
+ }
103
+ // send last heartbeat (wasClosed: true)
104
+ const data = { sessionId : this . sessionId } ;
105
+ const blob = new Blob ( [ JSON . stringify ( data ) ] , { type : "application/json" } ) ;
106
+ const gitpodHostUrl = new GitpodHostUrl ( new URL ( window . location . toString ( ) ) ) ;
107
+ const url = gitpodHostUrl . withApi ( { pathname : `/auth/workspacePageClose/${ this . instanceID } ` } ) . toString ( ) ;
108
+ navigator . sendBeacon ( url , blob ) ;
109
+ } ) ;
110
+ }
111
+
112
+ private async processServerInfo ( ) {
113
+ this . user = await this . service . server . getLoggedInUser ( ) ;
114
+ const workspace = await this . service . server . getWorkspace ( this . workspaceID ) ;
115
+ this . instanceID = workspace . latestInstance ?. id ;
116
+ if ( this . instanceID ) {
117
+ this . auth ( ) ;
118
+ }
119
+
120
+ const listener = await this . service . listenToInstance ( this . workspaceID ) ;
121
+ listener . onDidChange ( ( ) => {
122
+ this . ideUrl = listener . info . latestInstance ?. ideUrl
123
+ ? new URL ( listener . info . latestInstance ?. ideUrl )
124
+ : undefined ;
125
+ const status = this . getWorkspaceStatus ( listener . info ) ;
126
+ this . latestStatus = status ;
127
+ this . sendStatusUpdate ( this . latestStatus ) ;
128
+ if ( this . instanceID !== status . instanceId ) {
129
+ this . instanceID = status . instanceId ;
130
+ this . auth ( ) ;
131
+ }
132
+ } ) ;
133
+ }
134
+
135
+ getWorkspaceStatus ( workspace : WorkspaceInfo ) : IDEFrontendDashboardService . Status {
136
+ return {
137
+ loggedUserId : this . user ! . id ,
138
+ workspaceID : this . workspaceID ,
139
+ instanceId : workspace . latestInstance ?. id ,
140
+ ideUrl : workspace . latestInstance ?. ideUrl ,
141
+ statusPhase : workspace . latestInstance ?. status . phase ,
142
+ workspaceDescription : workspace . workspace . description ,
143
+ workspaceType : workspace . workspace . type ,
144
+ } ;
145
+ }
146
+
147
+ // implements
148
+
149
+ async auth ( ) {
150
+ if ( ! this . instanceID ) {
151
+ return ;
152
+ }
153
+ const url = gitpodHostUrl . asWorkspaceAuth ( this . instanceID ) . toString ( ) ;
154
+ await fetch ( url , {
155
+ credentials : "include" ,
156
+ } ) ;
157
+ }
158
+
159
+ trackEvent ( msg : RemoteTrackMessage ) : void {
160
+ msg . properties = {
161
+ ...msg . properties ,
162
+ sessionId : this . sessionId ,
163
+ instanceId : this . latestStatus ?. instanceId ,
164
+ workspaceId : this . workspaceID ,
165
+ type : this . latestStatus ?. workspaceType ,
166
+ } ;
167
+ this . service . server . trackEvent ( msg ) ;
168
+ }
169
+
170
+ activeHeartbeat ( ) : void {
171
+ if ( this . instanceID ) {
172
+ this . service . server . sendHeartBeat ( { instanceId : this . instanceID } ) ;
173
+ }
174
+ }
175
+
176
+ sendStatusUpdate ( status : IDEFrontendDashboardService . Status ) : void {
177
+ if ( ! this . ideUrl ) {
178
+ return ;
179
+ }
180
+ this . clientWindow . postMessage (
181
+ {
182
+ type : "ide-status-update" ,
183
+ status,
184
+ } as IDEFrontendDashboardService . StatusUpdateEventData ,
185
+ this . ideUrl . origin ,
186
+ ) ;
187
+ }
188
+
189
+ relocate ( url : string ) : void {
190
+ if ( ! this . ideUrl ) {
191
+ return ;
192
+ }
193
+ this . clientWindow . postMessage (
194
+ { type : "ide-relocate" , url } as IDEFrontendDashboardService . RelocateEventData ,
195
+ this . ideUrl . origin ,
196
+ ) ;
197
+ }
198
+ }
0 commit comments