Implement endpoint for getting recent directories
This commit is contained in:
parent
16bcf59cb0
commit
8793110941
|
@ -10,8 +10,17 @@ body {
|
||||||
background: #272727;
|
background: #272727;
|
||||||
color: #f4f4f4;
|
color: #f4f4f4;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji",
|
font-family:
|
||||||
"Segoe UI Emoji", "Segoe UI Symbol";
|
-apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
"Segoe UI",
|
||||||
|
Roboto,
|
||||||
|
Helvetica,
|
||||||
|
Arial,
|
||||||
|
sans-serif,
|
||||||
|
"Apple Color Emoji",
|
||||||
|
"Segoe UI Emoji",
|
||||||
|
"Segoe UI Symbol";
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,11 @@ export interface SessionResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RecentResponse {
|
export interface RecentResponse {
|
||||||
readonly recent: ReadonlyArray<Application>
|
readonly paths: string[]
|
||||||
readonly running: ReadonlyArray<Application>
|
}
|
||||||
|
|
||||||
|
export interface RunningResponse {
|
||||||
|
readonly applications: ReadonlyArray<Application>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HealthRequest {
|
export interface HealthRequest {
|
||||||
|
|
|
@ -19,6 +19,7 @@ export enum ApiEndpoint {
|
||||||
applications = "/applications",
|
applications = "/applications",
|
||||||
recent = "/recent",
|
recent = "/recent",
|
||||||
run = "/run",
|
run = "/run",
|
||||||
|
running = "/running",
|
||||||
session = "/session",
|
session = "/session",
|
||||||
status = "/status",
|
status = "/status",
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
import { field, logger } from "@coder/logger"
|
import { field, logger } from "@coder/logger"
|
||||||
import * as cp from "child_process"
|
import * as cp from "child_process"
|
||||||
|
import * as fs from "fs-extra"
|
||||||
import * as http from "http"
|
import * as http from "http"
|
||||||
import * as net from "net"
|
import * as net from "net"
|
||||||
|
import * as path from "path"
|
||||||
|
import * as url from "url"
|
||||||
import * as WebSocket from "ws"
|
import * as WebSocket from "ws"
|
||||||
import {
|
import {
|
||||||
Application,
|
Application,
|
||||||
ApplicationsResponse,
|
ApplicationsResponse,
|
||||||
ClientMessage,
|
ClientMessage,
|
||||||
RecentResponse,
|
RecentResponse,
|
||||||
|
RunningResponse,
|
||||||
ServerMessage,
|
ServerMessage,
|
||||||
SessionError,
|
SessionError,
|
||||||
SessionResponse,
|
SessionResponse,
|
||||||
|
@ -22,6 +26,12 @@ interface ServerSession {
|
||||||
readonly app: Application
|
readonly app: Application
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface VsRecents {
|
||||||
|
[key: string]: (string | object)[]
|
||||||
|
}
|
||||||
|
|
||||||
|
type VsSettings = [string, string][]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API HTTP provider.
|
* API HTTP provider.
|
||||||
*/
|
*/
|
||||||
|
@ -29,7 +39,11 @@ export class ApiHttpProvider extends HttpProvider {
|
||||||
private readonly ws = new WebSocket.Server({ noServer: true })
|
private readonly ws = new WebSocket.Server({ noServer: true })
|
||||||
private readonly sessions = new Map<string, ServerSession>()
|
private readonly sessions = new Map<string, ServerSession>()
|
||||||
|
|
||||||
public constructor(options: HttpProviderOptions, private readonly server: HttpServer) {
|
public constructor(
|
||||||
|
options: HttpProviderOptions,
|
||||||
|
private readonly server: HttpServer,
|
||||||
|
private readonly dataDir?: string,
|
||||||
|
) {
|
||||||
super(options)
|
super(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +74,11 @@ export class ApiHttpProvider extends HttpProvider {
|
||||||
return {
|
return {
|
||||||
content: await this.recent(),
|
content: await this.recent(),
|
||||||
} as HttpResponse<RecentResponse>
|
} as HttpResponse<RecentResponse>
|
||||||
|
case ApiEndpoint.running:
|
||||||
|
this.ensureMethod(request)
|
||||||
|
return {
|
||||||
|
content: await this.running(),
|
||||||
|
} as HttpResponse<RunningResponse>
|
||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
@ -280,12 +299,58 @@ export class ApiHttpProvider extends HttpProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return recent sessions.
|
* Return VS Code's recent paths.
|
||||||
*/
|
*/
|
||||||
public async recent(): Promise<RecentResponse> {
|
public async recent(): Promise<RecentResponse> {
|
||||||
|
try {
|
||||||
|
if (!this.dataDir) {
|
||||||
|
throw new Error("data directory is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
const state: VsSettings = JSON.parse(await fs.readFile(path.join(this.dataDir, "User/state/global.json"), "utf8"))
|
||||||
|
const setting = Array.isArray(state) && state.find((item) => item[0] === "recently.opened")
|
||||||
|
if (!setting) {
|
||||||
|
throw new Error("settings appear malformed")
|
||||||
|
}
|
||||||
|
|
||||||
|
const paths: { [key: string]: Promise<string> } = {}
|
||||||
|
Object.values(JSON.parse(setting[1]) as VsRecents).forEach((recents) => {
|
||||||
|
recents
|
||||||
|
.filter((recent) => typeof recent === "string")
|
||||||
|
.forEach((recent) => {
|
||||||
|
try {
|
||||||
|
const pathname = url.parse(recent as string).pathname
|
||||||
|
if (pathname && !paths[pathname]) {
|
||||||
|
paths[pathname] = new Promise<string>((resolve) => {
|
||||||
|
fs.stat(pathname)
|
||||||
|
.then(() => resolve(pathname))
|
||||||
|
.catch(() => resolve())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.debug("invalid path", field("path", recent))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
paths: await Promise.all(Object.values(paths)),
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code !== "ENOENT") {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { paths: [] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return running sessions.
|
||||||
|
*/
|
||||||
|
public async running(): Promise<RunningResponse> {
|
||||||
return {
|
return {
|
||||||
recent: [], // TODO
|
applications: Array.from(this.sessions).map(([sessionId, session]) => ({
|
||||||
running: Array.from(this.sessions).map(([sessionId, session]) => ({
|
|
||||||
...session.app,
|
...session.app,
|
||||||
sessionId,
|
sessionId,
|
||||||
})),
|
})),
|
||||||
|
|
|
@ -90,14 +90,14 @@ export class MainHttpProvider extends HttpProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getRoot(route: Route): Promise<HttpResponse> {
|
public async getRoot(route: Route): Promise<HttpResponse> {
|
||||||
const recent = await this.api.recent()
|
const running = await this.api.running()
|
||||||
const apps = await this.api.installedApplications()
|
const apps = await this.api.installedApplications()
|
||||||
const response = await this.getUtf8Resource(this.rootPath, "src/browser/pages/home.html")
|
const response = await this.getUtf8Resource(this.rootPath, "src/browser/pages/home.html")
|
||||||
response.content = response.content
|
response.content = response.content
|
||||||
.replace(/{{COMMIT}}/g, this.options.commit)
|
.replace(/{{COMMIT}}/g, this.options.commit)
|
||||||
.replace(/{{BASE}}/g, this.base(route))
|
.replace(/{{BASE}}/g, this.base(route))
|
||||||
.replace(/{{UPDATE:NAME}}/, await this.getUpdate())
|
.replace(/{{UPDATE:NAME}}/, await this.getUpdate())
|
||||||
.replace(/{{APP_LIST:RUNNING}}/, this.getAppRows(recent.running))
|
.replace(/{{APP_LIST:RUNNING}}/, this.getAppRows(running.applications))
|
||||||
.replace(
|
.replace(
|
||||||
/{{APP_LIST:EDITORS}}/,
|
/{{APP_LIST:EDITORS}}/,
|
||||||
this.getAppRows(apps.filter((app) => app.categories && app.categories.includes("Editor"))),
|
this.getAppRows(apps.filter((app) => app.categories && app.categories.includes("Editor"))),
|
||||||
|
|
|
@ -44,7 +44,7 @@ const main = async (args: Args): Promise<void> => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const httpServer = new HttpServer(options)
|
const httpServer = new HttpServer(options)
|
||||||
const api = httpServer.registerHttpProvider("/api", ApiHttpProvider, httpServer)
|
const api = httpServer.registerHttpProvider("/api", ApiHttpProvider, httpServer, args["user-data-dir"])
|
||||||
const update = httpServer.registerHttpProvider("/update", UpdateHttpProvider, !args["disable-updates"])
|
const update = httpServer.registerHttpProvider("/update", UpdateHttpProvider, !args["disable-updates"])
|
||||||
httpServer.registerHttpProvider("/vscode", VscodeHttpProvider, args)
|
httpServer.registerHttpProvider("/vscode", VscodeHttpProvider, args)
|
||||||
httpServer.registerHttpProvider("/login", LoginHttpProvider)
|
httpServer.registerHttpProvider("/login", LoginHttpProvider)
|
||||||
|
|
Loading…
Reference in New Issue