Files
flipper/desktop/flipper-dump/src/index.tsx
Michel Weststrate bca169df73 Move settings, launcherSettings, GKs to app / flipper-server-core
Summary:
This diff moves a lot of stuff from the client to the server. This diff is fairly large, as a lot of concept closely relate, although some things have split off to the earlier diffs in the stack, or are still to follow (like making intern requests).

This diff primarily moves reading and storing settings and GKs from client to server (both flipper and launcher settings). This means that settings are no longer persisted by Redux (which only exists on client). Most other changes are fallout from that. For now settings are just one big object, although we might need to separate settings that are only make sense in an Electron context. For example launcher settings.

Reviewed By: passy, aigoncharov

Differential Revision: D32498649

fbshipit-source-id: d842faf7a7f03774b621c7656e53a9127afc6192
2021-12-08 04:30:54 -08:00

261 lines
7.1 KiB
TypeScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import fs from 'fs';
import os from 'os';
import yargs from 'yargs';
import {
FlipperServerImpl,
loadLauncherSettings,
loadProcessConfig,
loadSettings,
} from 'flipper-server-core';
import {
ClientDescription,
Logger,
DeviceDescription,
setLoggerInstance,
} from 'flipper-common';
import path from 'path';
import {stdout} from 'process';
// eslint-disable-next-line
const packageJson = JSON.parse(fs.readFileSync('../package.json', 'utf-8'));
const argv = yargs
.usage('$0 [args]')
.options({
device: {
describe: 'The device name to listen to',
type: 'string',
},
client: {
describe: 'The application name to listen to',
type: 'string',
},
plugin: {
describe: 'Plugin id to listen to',
type: 'string',
},
// TODO: support filtering events
// TODO: support verbose mode
// TODO: support post processing messages
})
.version(packageJson.version)
.help()
// .strict()
.parse(process.argv.slice(1));
async function start(deviceTitle: string, appName: string, pluginId: string) {
return new Promise(async (_resolve, reject) => {
let device: DeviceDescription | undefined;
let deviceResolver: () => void;
const devicePromise: Promise<void> = new Promise((resolve) => {
deviceResolver = resolve;
});
let client: ClientDescription | undefined;
const logger = createLogger();
setLoggerInstance(logger);
// avoid logging to STDOUT!
console.log = console.error;
console.debug = () => {};
console.info = console.error;
// TODO: initialise FB user manager to be able to do certificate exchange
const server = new FlipperServerImpl(
{
env: process.env,
gatekeepers: {},
isProduction: false,
paths: {
staticPath: path.resolve(__dirname, '..', '..', 'static'),
tempPath: os.tmpdir(),
appPath: `/dev/null`,
homePath: `/dev/null`,
execPath: process.execPath,
desktopPath: `/dev/null`,
},
launcherSettings: await loadLauncherSettings(),
processConfig: loadProcessConfig(process.env),
settings: await loadSettings(),
validWebSocketOrigins: [],
},
logger,
);
logger.info(
`Waiting for device '${deviceTitle}' client '${appName}' plugin '${pluginId}' ...`,
);
server.on('notification', ({type, title, description}) => {
if (type === 'error') {
reject(new Error(`${title}: ${description}`));
}
});
server.on('server-error', reject);
server.on('device-connected', (deviceInfo) => {
logger.info(
`Detected device [${deviceInfo.os}] ${deviceInfo.title} ${deviceInfo.serial}`,
);
if (deviceInfo.title === deviceTitle) {
logger.info('Device matched');
device = deviceInfo;
deviceResolver();
}
});
server.on('device-disconnected', (deviceInfo) => {
if (device && deviceInfo.serial === device.serial) {
reject(new Error('Device disconnected: ' + deviceInfo.serial));
}
});
server.on('client-setup', (client) => {
logger.info(
`Connection attempt: ${client.appName} on ${client.deviceName}`,
);
});
server.on(
'client-connected',
async (clientDescription: ClientDescription) => {
// device matching is promisified, as we clients can arrive before device is detected
await devicePromise;
if (clientDescription.query.app === appName) {
if (clientDescription.query.device_id === device!.serial) {
logger.info(`Client matched: ${clientDescription.id}`);
client = clientDescription;
try {
// fetch plugins
const response = await server.exec(
'client-request-response',
client.id,
{
method: 'getPlugins',
},
);
logger.info(JSON.stringify(response));
if (response.error) {
reject(response.error);
return;
}
const plugins: string[] = (response.success as any).plugins;
logger.info('Detected plugins ' + plugins.join(','));
if (!plugins.includes(pluginId)) {
// TODO: what if it only registers later?
throw new Error(
`Plugin ${pluginId} was not registered on client ${client.id}`,
);
}
logger.info(`Starting plugin ` + pluginId);
const response2 = await server.exec(
'client-request-response',
client.id,
{
method: 'init',
params: {plugin: pluginId},
},
);
if (response2.error) {
reject(response2.error);
}
logger.info('Plugin initialised');
} catch (e) {
reject(e);
}
}
}
},
);
server.on('client-disconnected', ({id}) => {
if (id === client?.id) {
// TODO: maybe we need a flag to signal that this might be undesired?
logger.info('Target application disconnected, exiting...');
process.exit(0);
}
});
server.on('client-message', ({id, message}) => {
if (id === client?.id) {
const parsed = JSON.parse(message);
if (parsed.method === 'execute') {
if (parsed.params.api === pluginId) {
// TODO: customizable format
stdout.write(
`\n\n\n[${parsed.params.method}]\n${JSON.stringify(
parsed.params.params,
null,
2,
)}\n`,
);
}
} else {
logger.warn('Dropping message ', message);
}
}
});
server
.connect()
.then(() => {
logger.info(
'Flipper server started and accepting device / client connections',
);
})
.catch(reject);
});
}
function createLogger(): Logger {
return {
track() {
// no-op
},
trackTimeSince() {
// no-op
},
debug() {
// TODO: support this with a --verbose flag
},
error(...args: any[]) {
console.error(...args);
},
warn(...args: any[]) {
console.warn(...args);
},
info(...args: any[]) {
// we want to redirect info level logging to STDERR! So that STDOUT is used merely for plugin output
console.error(...args);
},
};
}
if (!argv.device) {
console.error('--device not specified');
process.exit(1);
}
if (!argv.client) {
console.error('--client not specified');
process.exit(1);
}
if (!argv.plugin) {
console.error('--plugin not specified');
process.exit(1);
}
start(argv.device!, argv.client!, argv.plugin!).catch((e) => {
// eslint-disable-next-line
console.error(e);
process.exit(1);
});