mirror of https://github.com/embarklabs/embark.git
feat(@embark/api): Add command `service api on/off`
Add support for `service api on/off` commands. Deprecate commands `api start/stop` in favor of `service api on/off`. `service api on` - Enables the API server serving Cockpit. Shows an error if the API server is already starting or started. `service api off` - Disables the API server serving Cockpit. Shows an error if the API server is already stopping or stopped. `api start` - This command has been deprecated in favor of `service api on` and will be removed in future versions. `api stop` - This command has been deprecated in favor of `service api off` and will be removed in future versions. `api:start` - This event has been deprecated and will be removed in future versions. `api:stop` - This event has been deprecated and will be removed in future versions.
This commit is contained in:
parent
3a07d34cc2
commit
634feb597a
|
@ -11,6 +11,7 @@ export default class Api {
|
|||
private port!: number;
|
||||
private api!: Server;
|
||||
private apiUrl!: string;
|
||||
private isServiceRegistered = false;
|
||||
|
||||
constructor(private embark: Embark, private options: any) {
|
||||
this.embark.events.emit("status", __("Starting API & Cockpit UI"));
|
||||
|
@ -22,54 +23,70 @@ export default class Api {
|
|||
|
||||
this.listenToCommands();
|
||||
this.registerConsoleCommands();
|
||||
this.init();
|
||||
});
|
||||
}
|
||||
|
||||
this.embark.events.request("processes:register", "api", {
|
||||
launchFn: (cb: (error: Error | null, message: string) => void) => {
|
||||
this.api.start()
|
||||
.then(() => cb(null, __("Cockpit UI available at %s", this.apiUrl)))
|
||||
.catch((error: Error) => cb(error, ""));
|
||||
},
|
||||
stopFn: (cb: (error: Error | null, message: string) => void) => {
|
||||
this.api.stop()
|
||||
.then(() => cb(null, __("Cockpit UI stopped")))
|
||||
.catch((error: Error) => cb(error, ""));
|
||||
},
|
||||
});
|
||||
private init() {
|
||||
this.embark.events.request("processes:register", "api", {
|
||||
launchFn: (cb: (error: Error | null, message: string) => void) => {
|
||||
this.api.start()
|
||||
.then(() => cb(null, __("Cockpit UI available at %s", this.apiUrl)))
|
||||
.catch((error: Error) => cb(error, ""));
|
||||
},
|
||||
stopFn: (cb: (error: Error | null, message: string) => void) => {
|
||||
this.api.stop()
|
||||
.then(() => cb(null, __("Cockpit UI stopped")))
|
||||
.catch((error: Error) => cb(error, ""));
|
||||
},
|
||||
});
|
||||
|
||||
this.embark.events.request("processes:launch", "api", (error: Error | null, message: string) => {
|
||||
if (error) {
|
||||
this.embark.logger.error(error.message);
|
||||
} else {
|
||||
this.embark.logger.info(message);
|
||||
}
|
||||
this.setServiceCheck();
|
||||
});
|
||||
this.embark.events.request("processes:launch", "api", (error: Error | null, message: string) => {
|
||||
if (error) {
|
||||
this.embark.logger.error(error.message);
|
||||
} else {
|
||||
this.embark.logger.info(message);
|
||||
}
|
||||
this.setServiceCheck();
|
||||
});
|
||||
|
||||
this.embark.events.on("check:wentOffline:api", () => {
|
||||
this.embark.logger.info(__("Cockpit is offline, please close Cockpit."));
|
||||
});
|
||||
this.embark.events.on("check:backOnline:api", () => {
|
||||
this.embark.logger.info(__("Cockpit is online, please open/refresh Cockpit."));
|
||||
});
|
||||
}
|
||||
|
||||
private setServiceCheck() {
|
||||
if (this.isServiceRegistered) {
|
||||
return;
|
||||
}
|
||||
this.isServiceRegistered = true;
|
||||
this.embark.events.request("services:register", "api", (cb: (options: object) => any) => {
|
||||
checkIsAvailable(this.apiUrl, (isAvailable: boolean) => {
|
||||
const devServer = __("Cockpit UI") + " (" + this.apiUrl + ")";
|
||||
const serverStatus = (isAvailable ? "on" : "off");
|
||||
return cb({name: devServer, status: serverStatus});
|
||||
return cb({ name: devServer, status: serverStatus });
|
||||
});
|
||||
});
|
||||
|
||||
this.embark.events.on("check:wentOffline:api", () => {
|
||||
this.embark.logger.info(__("Cockpit UI is offline"));
|
||||
});
|
||||
}
|
||||
|
||||
private listenToCommands() {
|
||||
this.embark.events.setCommandHandler("api:url", (cb) => cb(this.apiUrl));
|
||||
this.embark.events.setCommandHandler("api:start", (cb) => this.embark.events.request("processes:launch", "api", cb));
|
||||
this.embark.events.setCommandHandler("api:stop", (cb) => this.embark.events.request("processes:stop", "api", cb));
|
||||
this.embark.events.setCommandHandler("logs:api:enable", (cb) => {
|
||||
this.embark.events.setCommandHandler("api:start", (cb) => {
|
||||
this.embark.logger.warn(__("The event 'api:start' has been deprecated and will be removed in future versions."));
|
||||
this.embark.events.request("processes:launch", "api", cb);
|
||||
});
|
||||
this.embark.events.setCommandHandler("api:stop", (cb) => {
|
||||
this.embark.logger.warn(__("The event 'api:stop' has been deprecated and will be removed in future versions."));
|
||||
this.embark.events.request("processes:stop", "api", cb);
|
||||
});
|
||||
this.embark.events.setCommandHandler("logs:api:enable", (cb) => {
|
||||
this.api.enableLogging();
|
||||
cb();
|
||||
});
|
||||
this.embark.events.setCommandHandler("logs:api:disable", (cb) => {
|
||||
this.embark.events.setCommandHandler("logs:api:disable", (cb) => {
|
||||
this.api.disableLogging();
|
||||
cb();
|
||||
});
|
||||
|
@ -79,16 +96,24 @@ export default class Api {
|
|||
this.embark.registerConsoleCommand({
|
||||
description: __("Start or stop the API"),
|
||||
matches: ["api start"],
|
||||
process: (cmd: string, callback: () => void) => {
|
||||
this.embark.events.request("api:start", callback);
|
||||
process: (cmd: string, callback: (msg: string) => void) => {
|
||||
const message = __("The command 'api:start' has been deprecated in favor of 'service api on' and will be removed in future versions.");
|
||||
this.embark.logger.warn(message); // logs to Embark's console
|
||||
this.embark.events.request("processes:launch", "api", (err: string, msg: string) => {
|
||||
callback(err || msg); // logs to Cockpit's console
|
||||
});
|
||||
},
|
||||
usage: "api start/stop",
|
||||
});
|
||||
|
||||
this.embark.registerConsoleCommand({
|
||||
matches: ["api stop"],
|
||||
process: (cmd: string, callback: () => void) => {
|
||||
this.embark.events.request("api:stop", callback);
|
||||
process: (cmd: string, callback: (msg: string) => void) => {
|
||||
const message = __("The command 'api:stop' has been deprecated in favor of 'service api off' and will be removed in future versions.");
|
||||
this.embark.logger.warn(message); // logs to Embark's console
|
||||
this.embark.events.request("processes:stop", "api", (err: string, msg: string) => {
|
||||
callback(err || msg); // logs to Cockpit's console
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import expressWs from "express-ws";
|
|||
import findUp from "find-up";
|
||||
import helmet from "helmet";
|
||||
import * as http from "http";
|
||||
import * as net from "net";
|
||||
import * as path from "path";
|
||||
import * as ws from "ws";
|
||||
|
||||
|
@ -28,6 +29,8 @@ export default class Server {
|
|||
private isLogging: boolean = false;
|
||||
private server?: http.Server;
|
||||
|
||||
private openSockets = new Set<net.Socket>();
|
||||
|
||||
constructor(private embark: Embark, private port: number, private hostname: string, private plugins: Plugins) {
|
||||
this.expressInstance = this.initApp();
|
||||
this.embarkUiBuildDir = (findUp.sync("node_modules/embark-ui/build", {cwd: embarkPath()}) || embarkPath("node_modules/embark-ui/build"));
|
||||
|
@ -67,6 +70,15 @@ export default class Server {
|
|||
this.server = this.expressInstance.app.listen(this.port, this.hostname, () => {
|
||||
resolve();
|
||||
});
|
||||
|
||||
// keep track of our open websockets so we can destroy them
|
||||
// if the api server is shutdown
|
||||
this.server.on("connection", (socket) => {
|
||||
this.openSockets.add(socket);
|
||||
socket.on("close", () => {
|
||||
this.openSockets.delete(socket);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -77,6 +89,9 @@ export default class Server {
|
|||
return reject(new Error(message));
|
||||
}
|
||||
|
||||
// close any open sockets
|
||||
this.openSockets.forEach((socket) => socket.destroy());
|
||||
|
||||
this.server.close(() => {
|
||||
this.server = undefined;
|
||||
resolve();
|
||||
|
@ -198,17 +213,17 @@ export default class Server {
|
|||
instance.app.use(cors());
|
||||
|
||||
instance.app.use(bodyParser.json());
|
||||
instance.app.use(bodyParser.urlencoded({extended: true}));
|
||||
instance.app.use(bodyParser.urlencoded({ extended: true }));
|
||||
|
||||
instance.app.ws("/logs", (websocket: ws, _req: Request) => {
|
||||
this.embark.events.on("log", (level: string, message: string) => {
|
||||
websocket.send(JSON.stringify({msg: message, msg_clear: message.stripColors, logLevel: level}), () => {});
|
||||
websocket.send(JSON.stringify({ msg: message, msg_clear: message.stripColors, logLevel: level }), () => { });
|
||||
});
|
||||
});
|
||||
|
||||
if (this.plugins) {
|
||||
instance.app.get("/embark-api/plugins", (_req, res: Response) => {
|
||||
res.send(JSON.stringify(this.plugins.plugins.map((plugin) => ({name: plugin.name}))));
|
||||
res.send(JSON.stringify(this.plugins.plugins.map((plugin) => ({ name: plugin.name }))));
|
||||
});
|
||||
|
||||
const callDescriptions: CallDescription[] = this.plugins.getPluginsProperty("apiCalls", "apiCalls");
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export interface Logger {
|
||||
info(text: string): void;
|
||||
warn(text: string): void;
|
||||
error(text: string, ...args: Array<string|Error>): void;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import Contracts from '../components/Contracts';
|
|||
import ContractsList from '../components/ContractsList';
|
||||
import {getContracts} from "../reducers/selectors";
|
||||
import PageHead from "../components/PageHead";
|
||||
import Loading from '../components/Loading';
|
||||
import Error from '../components/Error';
|
||||
|
||||
const MAX_CONTRACTS = 10;
|
||||
|
||||
|
@ -78,16 +80,25 @@ class ContractsContainer extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {error, loading, mode, updatePageHeader} = this.props;
|
||||
if (error) {
|
||||
return <Error error={error} />;
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
this.resetNums();
|
||||
let ContractsComp;
|
||||
if (this.props.mode === "detail") {
|
||||
if (mode === "detail") {
|
||||
ContractsComp = Contracts
|
||||
} else if (this.props.mode === "list") {
|
||||
} else if (mode === "list") {
|
||||
ContractsComp = ContractsList
|
||||
}
|
||||
return (
|
||||
<React.Fragment>
|
||||
{this.props.updatePageHeader &&
|
||||
{updatePageHeader &&
|
||||
<PageHead title="Contracts"
|
||||
description="Summary of all deployed contracts" />}
|
||||
<ContractsComp contracts={this.currentContracts}
|
||||
|
@ -115,7 +126,9 @@ ContractsContainer.propTypes = {
|
|||
stopContracts: PropTypes.func,
|
||||
fiddleContracts: PropTypes.array,
|
||||
mode: PropTypes.string,
|
||||
updatePageHeader: PropTypes.bool
|
||||
updatePageHeader: PropTypes.bool,
|
||||
error: PropTypes.string,
|
||||
loading: PropTypes.bool
|
||||
};
|
||||
|
||||
ContractsContainer.defaultProps = {
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
} from 'reactstrap';
|
||||
|
||||
import {
|
||||
contracts as contractsAction,
|
||||
commands as commandsAction,
|
||||
commandSuggestions as commandSuggestionsAction,
|
||||
listenToProcessLogs,
|
||||
|
@ -22,7 +21,7 @@ import Console from '../components/Console';
|
|||
import {EMBARK_PROCESS_NAME, LOG_LIMIT} from '../constants';
|
||||
import PageHead from '../components/PageHead';
|
||||
import ServicesContainer from './ServicesContainer';
|
||||
import {getContracts, getProcesses, getProcessLogs, getServices, getCommandSuggestions} from "../reducers/selectors";
|
||||
import {getProcesses, getProcessLogs, getServices, getCommandSuggestions} from "../reducers/selectors";
|
||||
import ContractsContainer from "./ContractsContainer";
|
||||
|
||||
class HomeContainer extends Component {
|
||||
|
@ -45,7 +44,6 @@ class HomeContainer extends Component {
|
|||
this.props.fetchProcessLogs(processName, LOG_LIMIT);
|
||||
this.props.listenToProcessLogs(processName);
|
||||
|
||||
this.props.fetchContracts();
|
||||
this.setState({activeProcess: processName});
|
||||
}
|
||||
|
||||
|
@ -71,17 +69,14 @@ class HomeContainer extends Component {
|
|||
</Card>
|
||||
)} />
|
||||
|
||||
<DataWrapper shouldRender={this.props.contracts.length > 0} {...this.props} render={({contracts}) => (
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Deployed Contracts</CardTitle>
|
||||
<div style={{marginBottom: '1.5rem', overflow: 'auto'}}>
|
||||
<ContractsContainer contracts={contracts} mode="list" numContractsToDisplay={5} updatePageHeader={false} />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
)} />
|
||||
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Deployed Contracts</CardTitle>
|
||||
<div style={{marginBottom: '1.5rem', overflow: 'auto'}}>
|
||||
<ContractsContainer mode="list" numContractsToDisplay={5} updatePageHeader={false} />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
</React.Fragment>
|
||||
);
|
||||
|
@ -97,16 +92,13 @@ HomeContainer.propTypes = {
|
|||
stopProcessLogs: PropTypes.func,
|
||||
fetchProcessLogs: PropTypes.func,
|
||||
listenToProcessLogs: PropTypes.func,
|
||||
fetchContracts: PropTypes.func,
|
||||
services: PropTypes.array,
|
||||
contracts: PropTypes.array
|
||||
services: PropTypes.array
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
processes: getProcesses(state),
|
||||
services: getServices(state),
|
||||
contracts: getContracts(state),
|
||||
error: state.errorMessage,
|
||||
processLogs: getProcessLogs(state),
|
||||
commandSuggestions: getCommandSuggestions(state),
|
||||
|
@ -120,7 +112,6 @@ export default connect(
|
|||
postCommand: commandsAction.post,
|
||||
postCommandSuggestions: commandSuggestionsAction.post,
|
||||
fetchProcessLogs: processLogsAction.request,
|
||||
fetchContracts: contractsAction.request,
|
||||
listenToProcessLogs,
|
||||
stopProcessLogs
|
||||
}
|
||||
|
|
|
@ -31,11 +31,15 @@ class ServicesContainer extends Component {
|
|||
ServicesContainer.propTypes = {
|
||||
fetchServices: PropTypes.func,
|
||||
listenToServices: PropTypes.func,
|
||||
error: PropTypes.string,
|
||||
loading: PropTypes.bool
|
||||
};
|
||||
|
||||
function mapStateToProps(state, _props) {
|
||||
return {
|
||||
services: getServices(state)
|
||||
services: getServices(state),
|
||||
error: state.errorMessage,
|
||||
loading: state.loading
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue