This repository has been archived by the owner on Feb 4, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
FrontendConnector.ts
255 lines (216 loc) · 8.47 KB
/
FrontendConnector.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
import { EventEmitter } from "events";
import { IntifaceProtocols } from "intiface-protocols";
import { IntifaceConfigurationEventManager } from "./IntifaceConfigurationEventManager";
import { IntifaceConfiguration } from "./IntifaceConfiguration";
import { IntifaceUtils } from "./Utils";
import { IntifaceFrontendLogger } from "./IntifaceFrontendLogger";
import * as winston from "winston";
class PromiseFuncs {
public resolve: (aMsg: IntifaceProtocols.IntifaceBackendMessage) => void;
public reject: (aError: Error) => void;
}
// Sends messages from the frontend/GUI to the backend/server process.
export abstract class FrontendConnector extends EventEmitter {
// Used for creating message pairs, when needed.
private _msgId: number = 1;
private _taskMap: Map<number, PromiseFuncs> =
new Map<number, PromiseFuncs>();
private _logger: winston.Logger;
// State we need to manage from the Backend, that may not persist in the
// frontend components.
private _isServerProcessRunning: boolean = false;
private _clientName: string | null = null;
public get Config(): IntifaceConfiguration | null {
if (this._config === null) {
return null;
}
// This can be null up until we receive a configuration from the parent process.
return this._config!.Config;
}
private _config: IntifaceConfigurationEventManager | null = null;
protected constructor() {
super();
this._logger = IntifaceFrontendLogger.GetChildLogger(this.constructor.name);
IntifaceFrontendLogger.AddConnectorTransport(this);
}
public get ClientName(): string | null {
return this._clientName;
}
public get IsServerProcessRunning(): boolean {
return this._isServerProcessRunning;
}
public async LogMessage(aJsonMsg: string) {
const msg = IntifaceProtocols.IntifaceFrontendMessage.create({
logMessage: IntifaceProtocols.IntifaceFrontendMessage.LogMessage.create({
info: aJsonMsg,
}),
});
await this.SendMessageWithoutReturn(msg);
}
public async CheckForUpdates() {
const msg = IntifaceProtocols.IntifaceFrontendMessage.create({
checkForUpdates: IntifaceProtocols.IntifaceFrontendMessage.CheckForUpdates.create(),
});
await this.SendMessageExpectOk(msg);
}
public async UpdateDeviceFile() {
const msg = IntifaceProtocols.IntifaceFrontendMessage.create({
updateDeviceFile: IntifaceProtocols.IntifaceFrontendMessage.UpdateDeviceFile.create(),
});
await this.SendMessageExpectOk(msg);
}
public async UpdateEngine() {
const msg = IntifaceProtocols.IntifaceFrontendMessage.create({
updateEngine: IntifaceProtocols.IntifaceFrontendMessage.UpdateEngine.create(),
});
await this.SendMessageExpectOk(msg);
}
public async UpdateApplication() {
const msg = IntifaceProtocols.IntifaceFrontendMessage.create({
updateApplication: IntifaceProtocols.IntifaceFrontendMessage.UpdateApplication.create(),
});
await this.SendMessageExpectOk(msg);
}
public async StartProcess() {
const msg = IntifaceProtocols.IntifaceFrontendMessage.create({
startProcess: IntifaceProtocols.IntifaceFrontendMessage.StartProcess.create(),
});
await this.SendMessageExpectOk(msg);
}
public async StopProcess() {
const msg = IntifaceProtocols.IntifaceFrontendMessage.create({
stopProcess: IntifaceProtocols.IntifaceFrontendMessage.StopProcess.create(),
});
await this.SendMessageExpectOk(msg);
}
public async GenerateCertificate() {
const msg = IntifaceProtocols.IntifaceFrontendMessage.create({
generateCertificate: IntifaceProtocols.IntifaceFrontendMessage.GenerateCertificate.create(),
});
await this.SendMessageExpectOk(msg);
}
public async RunCertificateAcceptanceServer() {
const msg = IntifaceProtocols.IntifaceFrontendMessage.create({
runCertificateAcceptanceServer: IntifaceProtocols.IntifaceFrontendMessage.RunCertificateAcceptanceServer.create(),
});
await this.SendMessageExpectOk(msg);
}
protected abstract SendMessageInternal(aRawMsg: Buffer): void;
protected SendMessageWithoutReturn(aMsg: IntifaceProtocols.IntifaceFrontendMessage) {
this.SendMessageInternal(Buffer.from(IntifaceProtocols.IntifaceFrontendMessage.encode(aMsg).finish()));
}
protected async SendMessageExpectReturn(aMsg: IntifaceProtocols.IntifaceFrontendMessage)
: Promise<IntifaceProtocols.IntifaceBackendMessage> {
// Tag every outgoing message.
aMsg.index = this._msgId;
this._msgId += 1;
const [p, res, rej] = IntifaceUtils.MakePromise<IntifaceProtocols.IntifaceBackendMessage>();
const promiseFuncs = new PromiseFuncs();
promiseFuncs.resolve = res;
promiseFuncs.reject = rej;
this._taskMap.set(aMsg.index, promiseFuncs);
this.SendMessageWithoutReturn(aMsg);
const msg = await p;
if (msg.error !== null) {
throw msg.error;
}
return msg;
}
protected async SendMessageExpectOk(aMsg: IntifaceProtocols.IntifaceFrontendMessage): Promise<void> {
const msg = await this.SendMessageExpectReturn(aMsg);
// SendMessageExpectReturn will throw error if something returns error
// remotely, so in this case we've used the wrong function for message
// handling.
if (msg.ok === null) {
throw new Error("Expected to receive Ok, didn't get get it.");
}
return;
}
protected async Ready() {
const msg = IntifaceProtocols.IntifaceFrontendMessage.create({
ready: IntifaceProtocols.IntifaceFrontendMessage.Ready.create(),
});
const configMsg = await this.SendMessageExpectReturn(msg);
if (!configMsg.configuration) {
throw new Error("Didn't get configuration as expected. Cannot proceed.");
}
this._config = new IntifaceConfigurationEventManager(JSON.parse(configMsg.configuration!.configuration!));
// Any time the configuration is saved, throw it at the backend so we can
// update settings and save the file.
this._config.addListener("configsaved", (aConfig: string) => {
const updateConfigMsg = IntifaceProtocols.IntifaceFrontendMessage.create({
updateConfig: IntifaceProtocols.IntifaceFrontendMessage.UpdateConfig.create({ configuration: aConfig }),
});
this.SendMessageWithoutReturn(updateConfigMsg);
});
if (this._config.Config.CheckForUpdatesOnStart) {
// After we've sent ready and gotten back our configuration, trigger an
// update check in the background if we're supposed to check for updates
// at startup. This will do the check without hanging the GUI on startup.
process.nextTick(async () => {
await this.CheckForUpdates();
});
}
}
protected EmitServerMessage(aMsg: IntifaceProtocols.IntifaceBackendMessage) {
this.emit("message", aMsg);
}
protected ProcessMessage(aMsg: IntifaceProtocols.IntifaceBackendMessage) {
if (aMsg.index !== 0) {
if (!this._taskMap.has(aMsg.index)) {
this._logger.warn(`Got reply for message ${aMsg.index} with no matched promise.`);
} else {
// Save off our promise and delete from the map, then resolve it.
const p = this._taskMap.get(aMsg.index)!;
this._taskMap.delete(aMsg.index);
p.resolve(aMsg);
}
return;
}
if (aMsg.configuration !== null) {
if (!this._config) {
throw new Error("Configuration not set up yet, cannot update.");
}
this._config.Config.Load(JSON.parse(aMsg.configuration!.configuration!));
return;
}
if (aMsg.serverProcessMessage !== null) {
this.ProcessProcessMessage(aMsg.serverProcessMessage!);
return;
}
if (aMsg.downloadProgress) {
this.emit("progress", aMsg.downloadProgress);
}
if (aMsg.logMessage) {
const logObj = JSON.parse(aMsg.logMessage!.info!);
IntifaceFrontendLogger.Logger.log(logObj);
}
}
// Ah, the mistakes in naming that led to this point.
protected ProcessProcessMessage(aMsg: IntifaceProtocols.IServerProcessMessage) {
if (aMsg.processLog !== null) {
// This should be done in the parent process.
this._logger.log({
message: aMsg.processLog!.message!,
level: "info",
logType: "process",
location: "Process",
});
return;
}
if (aMsg.processStarted) {
this._isServerProcessRunning = true;
return;
}
if (aMsg.processEnded) {
this._isServerProcessRunning = false;
return;
}
if (aMsg.clientConnected) {
this._clientName = aMsg.clientConnected!.clientName!;
}
if (aMsg.clientDisconnected) {
this._clientName = null;
}
}
}