Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 02a19c0

Browse files
committed
chore(code.angularjs): enable directory listings
1 parent 6e6e4ee commit 02a19c0

File tree

2 files changed

+117
-30
lines changed

2 files changed

+117
-30
lines changed

scripts/code.angularjs.org-firebase/functions/index.js

Lines changed: 117 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ const gcs = require('@google-cloud/storage')();
55
const path = require('path');
66

77
const gcsBucketId = `${process.env.GCLOUD_PROJECT}.appspot.com`;
8-
const LOCAL_TMP_FOLDER = '/tmp/';
98

109
const BROWSER_CACHE_DURATION = 300;
1110
const CDN_CACHE_DURATION = 600;
@@ -22,7 +21,6 @@ function sendStoredFile(request, response) {
2221
const bucket = gcs.bucket(gcsBucketId);
2322

2423
let downloadSource;
25-
let downloadDestination;
2624
let fileName;
2725

2826
if (isDocsPath && filePathSegments.length === 2) {
@@ -32,43 +30,142 @@ function sendStoredFile(request, response) {
3230
fileName = lastSegment;
3331
}
3432

33+
if (!fileName) {
34+
//Root
35+
return getDirectoryListing('/').catch(sendErrorResponse);
36+
}
37+
3538
downloadSource = path.join.apply(null, filePathSegments);
36-
downloadDestination = `${LOCAL_TMP_FOLDER}${fileName}`;
3739

38-
downloadAndSend(downloadSource, downloadDestination).catch(error => {
40+
downloadAndSend(downloadSource).catch(error => {
3941
if (isDocsPath && error.code === 404) {
4042
fileName = 'index.html';
4143
filePathSegments = [version, 'docs', fileName];
4244
downloadSource = path.join.apply(null, filePathSegments);
43-
downloadDestination = `${LOCAL_TMP_FOLDER}${fileName}`;
4445

45-
return downloadAndSend(downloadSource, downloadDestination);
46+
return downloadAndSend(downloadSource);
4647
}
4748

4849
return Promise.reject(error);
4950
}).catch(error => {
50-
let message = 'General error';
51+
52+
// If file not found, try the path as a directory
53+
return error.code === 404 ? getDirectoryListing(request.path.slice(1)) : Promise.reject(error);
54+
}).catch(sendErrorResponse);
55+
56+
function downloadAndSend(downloadSource) {
57+
58+
const file = bucket.file(downloadSource);
59+
60+
return file.getMetadata().then(data => {
61+
return new Promise((resolve, reject) => {
62+
63+
const readStream = file.createReadStream()
64+
.on('error', error => {
65+
reject(error);
66+
})
67+
.on('response', () => {
68+
resolve(response);
69+
});
70+
71+
response
72+
.status(200)
73+
.set({
74+
'Content-Type': data[0].contentType,
75+
'Cache-Control': `public, max-age=${BROWSER_CACHE_DURATION}, s-maxage=${CDN_CACHE_DURATION}`
76+
});
77+
78+
readStream.pipe(response);
79+
});
80+
81+
});
82+
}
83+
84+
function sendErrorResponse(error) {
85+
let code = 500;
86+
let message = `General error. Please try again later.
87+
If the error persists, please create an issue in the
88+
<a href="https://github.com/angular/angular.js/issues">AngularJS Github repository</a>`;
89+
5190
if (error.code === 404) {
52-
if (fileName.split('.').length === 1) {
53-
message = 'Directory listing is not supported';
54-
} else {
55-
message = 'File not found';
56-
}
91+
message = 'File or directory not found';
92+
code = 404;
5793
}
5894

59-
return response.status(error.code).send(message);
60-
});
95+
return response.status(code).send(message);
96+
}
97+
98+
function getDirectoryListing(path) {
99+
if (!path.endsWith('/')) path += '/';
61100

62-
function downloadAndSend(downloadSource, downloadDestination) {
63-
return bucket.file(downloadSource).download({
64-
destination: downloadDestination
65-
}).then(() => {
66-
return response.status(200)
101+
const getFilesOptions = {
102+
delimiter: '/',
103+
autoPaginate: false
104+
};
105+
106+
if (path !== '/') getFilesOptions.prefix = path;
107+
108+
let fileList = [];
109+
let directoryList = [];
110+
111+
return getContent(getFilesOptions).then(() => {
112+
let contentList = '';
113+
114+
directoryList.forEach(directoryPath => {
115+
const dirName = directoryPath.split('/').reverse()[1];
116+
contentList += `<a href="${dirName}/">${dirName}/</a><br>`;
117+
});
118+
119+
fileList.forEach(file => {
120+
const fileName = file.metadata.name.split('/').pop();
121+
contentList += `<a href="${fileName}">${fileName}</a><br>`;
122+
});
123+
124+
// A trailing slash in the base creates correct relative links when the url is accessed
125+
// without trailing slash
126+
const base = request.originalUrl.endsWith('/') ? request.originalUrl : request.originalUrl + '/';
127+
128+
let directoryListing = `
129+
<base href="${base}">
130+
<h1>Index of ${path}</h1>
131+
<hr>
132+
<pre>${contentList}</pre>`;
133+
134+
return response
135+
.status(200)
67136
.set({
68137
'Cache-Control': `public, max-age=${BROWSER_CACHE_DURATION}, s-maxage=${CDN_CACHE_DURATION}`
69138
})
70-
.sendFile(downloadDestination);
139+
.send(directoryListing);
71140
});
141+
142+
function getContent(options) {
143+
return bucket.getFiles(options).then(data => {
144+
const files = data[0];
145+
const nextQuery = data[1];
146+
const apiResponse = data[2];
147+
148+
if (!files.length && (!apiResponse || !apiResponse.prefixes)) {
149+
return Promise.reject({
150+
code: 404
151+
});
152+
}
153+
154+
fileList = fileList.concat(files);
155+
156+
if (apiResponse && apiResponse.prefixes) {
157+
directoryList = directoryList.concat(apiResponse.prefixes);
158+
}
159+
160+
if (nextQuery) {
161+
// If the results are paged, get the next page
162+
return getContent(nextQuery);
163+
}
164+
165+
return true;
166+
});
167+
168+
}
72169
}
73170
}
74171

scripts/code.angularjs.org-firebase/public/index.html

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)