Add the ability to kill running VS Code instance
This commit is contained in:
parent
fd65cadaea
commit
21cfeb9da0
@ -26,3 +26,7 @@
|
||||
.error-display > .links > .link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.error-display .success {
|
||||
color: green;
|
||||
}
|
||||
|
@ -32,28 +32,15 @@
|
||||
}
|
||||
|
||||
.block-row > .item > .icon.-missing {
|
||||
background-color: rgb(87, 114, 245);
|
||||
color: #fff;
|
||||
background-color: rgba(87, 114, 245, 0.2);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.block-row > .item > .icon.-missing::after {
|
||||
content: "?";
|
||||
font-size: 0.7rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.kill-form {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.kill-form > .kill {
|
||||
background-color: rgb(87, 114, 245);
|
||||
border: none;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
line-height: 1rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-radius: 3px;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
@ -20,27 +20,36 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="center-container">
|
||||
<!-- TEMP: Only VS Code for now. -->
|
||||
<!-- <div class="info-block"> -->
|
||||
<!-- <h2 class="header">Running Applications</h2> -->
|
||||
<!-- {{APP_LIST:RUNNING}} -->
|
||||
<!-- </div> -->
|
||||
<div class="card-box">
|
||||
<div class="header">
|
||||
<h2 class="main">Running</h2>
|
||||
<div class="sub">Currently running applications.</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
{{APP_LIST:RUNNING}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-box">
|
||||
<div class="header">
|
||||
<h2 class="main">Launch</h2>
|
||||
<div class="sub">Choose an application to launch below.</div>
|
||||
<h2 class="main">Editors</h2>
|
||||
<div class="sub">Choose an editor to launch below.</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="block-row">
|
||||
<a class="item -link" href="./vscode">
|
||||
<img class="icon" src="./static-{{COMMIT}}/lib/vscode/resources/linux/code.png" />
|
||||
VS Code
|
||||
</a>
|
||||
</div>
|
||||
{{APP_LIST:EDITORS}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="card-box"> -->
|
||||
<!-- <div class="header"> -->
|
||||
<!-- <h2 class="main">Other</h2> -->
|
||||
<!-- <div class="sub">Choose an application to launch below.</div> -->
|
||||
<!-- </div> -->
|
||||
<!-- <div class="content"> -->
|
||||
<!-- {{APP_LIST:OTHER}} -->
|
||||
<!-- </div> -->
|
||||
<!-- </div> -->
|
||||
|
||||
<div class="card-box">
|
||||
<div class="header">
|
||||
<h2 class="main">Version</h2>
|
||||
@ -50,16 +59,6 @@
|
||||
{{UPDATE:NAME}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="info-block"> -->
|
||||
<!-- <h2 class="header">Editors</h2> -->
|
||||
<!-- {{APP_LIST:EDITORS}} -->
|
||||
<!-- </div> -->
|
||||
|
||||
<!-- <div class="info-block"> -->
|
||||
<!-- <h2 class="header">Other</h2> -->
|
||||
<!-- {{APP_LIST:OTHER}} -->
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -19,7 +19,8 @@ import {
|
||||
import { ApiEndpoint, HttpCode } from "../../common/http"
|
||||
import { normalize } from "../../common/util"
|
||||
import { HttpProvider, HttpProviderOptions, HttpResponse, HttpServer, Route } from "../http"
|
||||
import { findApplications, findWhitelistedApplications } from "./bin"
|
||||
import { findApplications, findWhitelistedApplications, Vscode } from "./bin"
|
||||
import { VscodeHttpProvider } from "./vscode"
|
||||
|
||||
interface ServerSession {
|
||||
process?: cp.ChildProcess
|
||||
@ -42,6 +43,7 @@ export class ApiHttpProvider extends HttpProvider {
|
||||
public constructor(
|
||||
options: HttpProviderOptions,
|
||||
private readonly server: HttpServer,
|
||||
private readonly vscode: VscodeHttpProvider,
|
||||
private readonly dataDir?: string,
|
||||
) {
|
||||
super(options)
|
||||
@ -256,17 +258,24 @@ export class ApiHttpProvider extends HttpProvider {
|
||||
/**
|
||||
* Kill a session identified by `app.sessionId`.
|
||||
*/
|
||||
public deleteSession(sessionId: string): HttpResponse {
|
||||
public async deleteSession(sessionId: string): Promise<HttpResponse> {
|
||||
logger.debug("deleting session", field("sessionId", sessionId))
|
||||
const session = this.sessions.get(sessionId)
|
||||
if (!session) {
|
||||
throw new Error("session does not exist")
|
||||
switch (sessionId) {
|
||||
case "vscode":
|
||||
await this.vscode.dispose()
|
||||
return { code: HttpCode.Ok }
|
||||
default: {
|
||||
const session = this.sessions.get(sessionId)
|
||||
if (!session) {
|
||||
throw new Error("session does not exist")
|
||||
}
|
||||
if (session.process) {
|
||||
session.process.kill()
|
||||
}
|
||||
this.sessions.delete(sessionId)
|
||||
return { code: HttpCode.Ok }
|
||||
}
|
||||
}
|
||||
if (session.process) {
|
||||
session.process.kill()
|
||||
}
|
||||
this.sessions.delete(sessionId)
|
||||
return { code: HttpCode.Ok }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -350,10 +359,20 @@ export class ApiHttpProvider extends HttpProvider {
|
||||
*/
|
||||
public async running(): Promise<RunningResponse> {
|
||||
return {
|
||||
applications: Array.from(this.sessions).map(([sessionId, session]) => ({
|
||||
...session.app,
|
||||
sessionId,
|
||||
})),
|
||||
applications: (this.vscode.running
|
||||
? [
|
||||
{
|
||||
...Vscode,
|
||||
sessionId: "vscode",
|
||||
},
|
||||
]
|
||||
: []
|
||||
).concat(
|
||||
Array.from(this.sessions).map(([sessionId, session]) => ({
|
||||
...session.app,
|
||||
sessionId,
|
||||
})),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,7 +124,9 @@ export class MainHttpProvider extends HttpProvider {
|
||||
}
|
||||
|
||||
private getAppRows(apps: ReadonlyArray<Application>): string {
|
||||
return apps.length > 0 ? apps.map((app) => this.getAppRow(app)).join("\n") : `<div class="none">None</div>`
|
||||
return apps.length > 0
|
||||
? apps.map((app) => this.getAppRow(app)).join("\n")
|
||||
: `<div class="none">No applications are currently running.</div>`
|
||||
}
|
||||
|
||||
private getAppRow(app: Application): string {
|
||||
@ -141,7 +143,7 @@ export class MainHttpProvider extends HttpProvider {
|
||||
app.sessionId
|
||||
? `<form class="kill-form" action="./delete" method="POST">
|
||||
<input type="hidden" name="sessionId" value="${app.sessionId}">
|
||||
<button class="kill" type="submit">Kill</button>
|
||||
<button class="kill -button" type="submit">Kill</button>
|
||||
</form>`
|
||||
: ""
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import * as fs from "fs"
|
||||
import * as path from "path"
|
||||
import { Application } from "../../common/api"
|
||||
|
||||
@ -11,6 +12,7 @@ const getVscodeVersion = (): string => {
|
||||
|
||||
export const Vscode: Application = {
|
||||
categories: ["Editor"],
|
||||
icon: fs.readFileSync(path.resolve(__dirname, "../../../lib/vscode/resources/linux/code.png")).toString("base64"),
|
||||
installed: true,
|
||||
name: "VS Code",
|
||||
path: "/vscode",
|
||||
|
@ -31,6 +31,19 @@ export class VscodeHttpProvider extends HttpProvider {
|
||||
this.serverRootPath = path.join(this.vsRootPath, "out/vs/server")
|
||||
}
|
||||
|
||||
public get running(): boolean {
|
||||
return !!this._vscode
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
if (this._vscode) {
|
||||
const vscode = await this._vscode
|
||||
vscode.removeAllListeners()
|
||||
this._vscode = undefined
|
||||
vscode.kill()
|
||||
}
|
||||
}
|
||||
|
||||
private async initialize(options: VscodeOptions): Promise<WorkbenchOptions> {
|
||||
const id = generateUuid()
|
||||
const vscode = await this.fork()
|
||||
@ -126,7 +139,8 @@ export class VscodeHttpProvider extends HttpProvider {
|
||||
} catch (error) {
|
||||
const message = `<div>VS Code failed to load.</div> ${
|
||||
this.isDev
|
||||
? "<div>It might not have finished compiling.</div>Check for 'Finished compilation' in the output."
|
||||
? `<div>It might not have finished compiling.</div>` +
|
||||
`Check for <code>Finished <span class="success">compilation</span></code> in the output.`
|
||||
: ""
|
||||
} <br><br>${error}`
|
||||
return this.getErrorRoot(route, "VS Code failed to load", "500", message)
|
||||
|
@ -44,9 +44,9 @@ const main = async (args: Args): Promise<void> => {
|
||||
}
|
||||
|
||||
const httpServer = new HttpServer(options)
|
||||
const api = httpServer.registerHttpProvider("/api", ApiHttpProvider, httpServer, args["user-data-dir"])
|
||||
const vscode = httpServer.registerHttpProvider("/vscode", VscodeHttpProvider, args)
|
||||
const api = httpServer.registerHttpProvider("/api", ApiHttpProvider, httpServer, vscode, args["user-data-dir"])
|
||||
const update = httpServer.registerHttpProvider("/update", UpdateHttpProvider, !args["disable-updates"])
|
||||
httpServer.registerHttpProvider("/vscode", VscodeHttpProvider, args)
|
||||
httpServer.registerHttpProvider("/login", LoginHttpProvider)
|
||||
httpServer.registerHttpProvider("/", MainHttpProvider, api, update)
|
||||
|
||||
|
@ -360,6 +360,10 @@ export interface HttpProvider2<A1, A2, T> {
|
||||
new (options: HttpProviderOptions, a1: A1, a2: A2): T
|
||||
}
|
||||
|
||||
export interface HttpProvider3<A1, A2, A3, T> {
|
||||
new (options: HttpProviderOptions, a1: A1, a2: A2, a3: A3): T
|
||||
}
|
||||
|
||||
/**
|
||||
* An HTTP server. Its main role is to route incoming HTTP requests to the
|
||||
* appropriate provider for that endpoint then write out the response. It also
|
||||
@ -417,6 +421,13 @@ export class HttpServer {
|
||||
a1: A1,
|
||||
a2: A2,
|
||||
): T
|
||||
public registerHttpProvider<A1, A2, A3, T extends HttpProvider>(
|
||||
endpoint: string,
|
||||
provider: HttpProvider3<A1, A2, A3, T>,
|
||||
a1: A1,
|
||||
a2: A2,
|
||||
a3: A3,
|
||||
): T
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public registerHttpProvider(endpoint: string, provider: any, ...args: any[]): any {
|
||||
endpoint = endpoint.replace(/^\/+|\/+$/g, "")
|
||||
|
Reference in New Issue
Block a user