@@ -4,8 +4,11 @@ import { Server, ServerOptions } from "@coder/protocol/src/node/server";
44import { NewSessionMessage } from '@coder/protocol/src/proto' ;
55import { ChildProcess } from "child_process" ;
66import * as express from "express" ;
7+ import * as fs from "fs" ;
78import * as http from "http" ;
9+ import * as mime from "mime-types" ;
810import * as path from "path" ;
11+ import * as util from "util" ;
912import * as ws from "ws" ;
1013import { forkModule } from "./vscode/bootstrapFork" ;
1114
@@ -63,6 +66,43 @@ export const createApp = (registerMiddleware?: (app: express.Application) => voi
6366
6467 app . use ( express . static ( path . join ( __dirname , "../build/web" ) ) ) ;
6568
69+ app . get ( "/resource/:url(*)" , async ( req , res ) => {
70+ try {
71+ const fullPath = `/${ req . params . url } ` ;
72+ const relative = path . relative ( options ! . dataDirectory , fullPath ) ;
73+ if ( relative . startsWith ( ".." ) ) {
74+ return res . status ( 403 ) . end ( ) ;
75+ }
76+ const exists = await util . promisify ( fs . exists ) ( fullPath ) ;
77+ if ( ! exists ) {
78+ res . status ( 404 ) . end ( ) ;
79+ return ;
80+ }
81+ const stat = await util . promisify ( fs . stat ) ( fullPath ) ;
82+ if ( ! stat . isFile ( ) ) {
83+ res . write ( "Resource must be a file." ) ;
84+ res . status ( 422 ) ;
85+ res . end ( ) ;
86+
87+ return ;
88+ }
89+ let mimeType = mime . lookup ( fullPath ) ;
90+ if ( mimeType === false ) {
91+ mimeType = "application/octet-stream" ;
92+ }
93+ const content = await util . promisify ( fs . readFile ) ( fullPath ) ;
94+
95+ res . header ( "Content-Type" , mimeType as string ) ;
96+ res . write ( content ) ;
97+ res . status ( 200 ) ;
98+ res . end ( ) ;
99+ } catch ( ex ) {
100+ res . write ( ex . toString ( ) ) ;
101+ res . status ( 500 ) ;
102+ res . end ( ) ;
103+ }
104+ } ) ;
105+
66106 return {
67107 express : app ,
68108 server,
0 commit comments