import * as http from "http" import * as querystring from "querystring" import { Application, RecentResponse } from "../../common/api" import { HttpCode, HttpError } from "../../common/http" import { HttpProvider, HttpProviderOptions, HttpResponse, Route } from "../http" import { ApiHttpProvider } from "./api" import { Vscode } from "./bin" import { UpdateHttpProvider } from "./update" /** * Dashboard HTTP provider. */ export class DashboardHttpProvider extends HttpProvider { public constructor( options: HttpProviderOptions, private readonly api: ApiHttpProvider, private readonly update: UpdateHttpProvider, ) { super(options) } public async handleRequest(route: Route, request: http.IncomingMessage): Promise { if (route.requestPath !== "/index.html") { throw new HttpError("Not found", HttpCode.NotFound) } switch (route.base) { case "/delete": { this.ensureAuthenticated(request) this.ensureMethod(request, "POST") const data = await this.getData(request) const p = data ? querystring.parse(data) : {} this.api.deleteSession(p.sessionId as string) return { redirect: this.options.base } } case "/": { this.ensureMethod(request) if (!this.authenticated(request)) { return { redirect: "/login", query: { to: this.options.base } } } return this.getRoot(route) } } throw new HttpError("Not found", HttpCode.NotFound) } public async getRoot(route: Route): Promise { const base = this.base(route) const apps = await this.api.installedApplications() const response = await this.getUtf8Resource(this.rootPath, "src/browser/pages/home.html") response.content = response.content .replace(/{{UPDATE:NAME}}/, await this.getUpdate(base)) .replace(/{{APP_LIST:RUNNING}}/, this.getAppRows(base, (await this.api.running()).applications)) .replace(/{{APP_LIST:RECENT_PROJECTS}}/, this.getRecentProjectRows(base, await this.api.recent())) .replace( /{{APP_LIST:EDITORS}}/, this.getAppRows( base, apps.filter((app) => app.categories && app.categories.includes("Editor")), ), ) .replace( /{{APP_LIST:OTHER}}/, this.getAppRows( base, apps.filter((app) => !app.categories || !app.categories.includes("Editor")), ), ) return this.replaceTemplates(route, response) } private getRecentProjectRows(base: string, recents: RecentResponse): string { return recents.paths.length > 0 || recents.workspaces.length > 0 ? recents.paths.map((recent) => this.getRecentProjectRow(base, recent)).join("\n") + recents.workspaces.map((recent) => this.getRecentProjectRow(base, recent, true)).join("\n") : `
No recent directories or workspaces.
` } private getRecentProjectRow(base: string, recent: string, workspace?: boolean): string { return `` } private getAppRows(base: string, apps: ReadonlyArray): string { return apps.length > 0 ? apps.map((app) => this.getAppRow(base, app)).join("\n") : `
No applications are currently running.
` } private getAppRow(base: string, app: Application): string { return `
${ app.icon ? `` : `
` }
${app.name}
${ app.sessionId ? `
` : "" }
` } private async getUpdate(base: string): Promise { if (!this.update.enabled) { return `
Updates are disabled
` } const humanize = (time: number): string => { const d = new Date(time) const pad = (t: number): string => (t < 10 ? "0" : "") + t return ( `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}` + ` ${pad(d.getHours())}:${pad(d.getMinutes())}` ) } const update = await this.update.getUpdate() if (this.update.isLatestVersion(update)) { return `
Latest: ${update.version}
Up to date
${humanize(update.checked)} Check now
Current: ${this.update.currentVersion}
` } return `
Latest: ${update.version}
Out of date
${humanize(update.checked)} Update now
Current: ${this.update.currentVersion}
` } }