@@ -4,8 +4,11 @@ import { Server, ServerOptions } from "@coder/protocol/src/node/server";
4
4
import { NewSessionMessage } from '@coder/protocol/src/proto' ;
5
5
import { ChildProcess } from "child_process" ;
6
6
import * as express from "express" ;
7
+ import * as fs from "fs" ;
7
8
import * as http from "http" ;
9
+ import * as mime from "mime-types" ;
8
10
import * as path from "path" ;
11
+ import * as util from "util" ;
9
12
import * as ws from "ws" ;
10
13
import { forkModule } from "./vscode/bootstrapFork" ;
11
14
@@ -63,6 +66,43 @@ export const createApp = (registerMiddleware?: (app: express.Application) => voi
63
66
64
67
app . use ( express . static ( path . join ( __dirname , "../build/web" ) ) ) ;
65
68
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
+
66
106
return {
67
107
express : app ,
68
108
server,
0 commit comments