Skip to content

Commit 9b33606

Browse files
committed
Add TTL support
1 parent 4fb48ce commit 9b33606

File tree

5 files changed

+91
-21
lines changed

5 files changed

+91
-21
lines changed

src/commands/serve.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {createApp} from '../server';
33
interface ServeOptions {
44
site: string;
55
ref?: string;
6+
fcd?: string;
67
}
78

89
export class ServeCommand {

src/commands/upload.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ interface UploadOptions {
88
site: string;
99
ref?: string;
1010
branch?: string;
11+
ttl?: string;
1112
}
1213

1314
export class UploadCommand {
@@ -27,6 +28,12 @@ export class UploadCommand {
2728
console.log(`No files found in -> ${path}`);
2829
return;
2930
}
30-
upload.uploadManifest(this.options.bucket, manifest, this.options.force);
31+
const ttl = this.options.ttl ? new Date(this.options.ttl) : undefined;
32+
upload.uploadManifest(
33+
this.options.bucket,
34+
manifest,
35+
this.options.force,
36+
ttl
37+
);
3138
}
3239
}

src/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@ const program = createCommand();
99
program
1010
.command('upload [dir]')
1111
.description('Uploads a directory to cloud storage')
12-
.option('-b, --bucket <bucket>', 'bucket', '')
1312
.option('-s, --site <site>', 'site', '')
1413
.option('-r, --ref <ref>', 'ref', '')
15-
.option('-f, --force', '', false)
16-
.option('-br, --branch <branch>', 'branch', '')
14+
.option('-f, --force', 'force', false)
15+
.option('-t, --ttl <ttl>', 'ttl', undefined)
1716
.action((path, options) => {
1817
const cmd = new UploadCommand(options);
1918
cmd.run(path);

src/server.ts

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {GoogleAuth} from 'google-auth-library';
33
import * as fsPath from 'path';
44
import express = require('express');
55
import httpProxy = require('http-proxy');
6-
import { Manifest } from './manifest';
6+
import {Manifest} from './manifest';
77

88
const URL = 'https://storage.googleapis.com';
99
const datastore = new Datastore();
@@ -26,7 +26,33 @@ const downloadManifest = async (branchOrRef: string) => {
2626
return;
2727
}
2828
const entities = resp[0];
29-
return entities[0] || entities[1];
29+
const result = entities[0] || entities[1];
30+
if (!result) {
31+
return;
32+
}
33+
if (!result.ttls) {
34+
return result;
35+
}
36+
37+
if (result.ttls) {
38+
// TODO: Allow this to be overwritten.
39+
const now = new Date();
40+
let latestManifest = null;
41+
for (const ttlString in result.ttls) {
42+
const ttlDate = new Date(ttlString);
43+
const isLaterThanManifestDate = now >= ttlDate;
44+
const isLaterThanAllManifests =
45+
!latestManifest || ttlDate >= latestManifest.ttl;
46+
if (isLaterThanManifestDate && isLaterThanAllManifests) {
47+
latestManifest = result.ttls[ttlString];
48+
latestManifest.ttl = ttlDate;
49+
}
50+
}
51+
if (latestManifest) {
52+
return latestManifest;
53+
}
54+
}
55+
return result;
3056
};
3157

3258
const parseHostname = (hostname: string) => {
@@ -63,6 +89,12 @@ export function createApp(siteId: string, branchOrRef: string) {
6389
}
6490

6591
const manifest = await downloadManifest(requestBranchOrRef);
92+
if (!manifest) {
93+
// TODO: Consolidate not found errors.
94+
res.sendStatus(404);
95+
return;
96+
}
97+
6698
const manifestPaths = manifest.paths;
6799

68100
if (!manifestPaths) {
@@ -89,6 +121,9 @@ export function createApp(siteId: string, branchOrRef: string) {
89121
changeOrigin: true,
90122
preserveHeaderKeyCase: true,
91123
});
124+
server.on('error', (error, req, res) => {
125+
console.log(`An error occurred while serving ${req.url}`, error);
126+
});
92127
server.on('proxyRes', (proxyRes, req, res) => {
93128
delete proxyRes.headers['x-cloud-trace-context'];
94129
delete proxyRes.headers['x-goog-generation'];
@@ -103,10 +138,13 @@ export function createApp(siteId: string, branchOrRef: string) {
103138
delete proxyRes.headers['x-guploader-uploadid'];
104139
// This cannot be "private, max-age=0" as this kills perf.
105140
// This also can't be a very small value, as it kills perf. 0036 seems to work correctly.
106-
proxyRes.headers['cache-control'] = 'public, max-age=0036'; // The padded 0036 keeps the content length the same per upload.ts.
141+
proxyRes.headers['cache-control'] = 'public, max-age=0036'; // The padded 0036 keeps the content length the same per upload.ts.
107142
proxyRes.headers['x-fileset-blob'] = blobKey;
108143
proxyRes.headers['x-fileset-ref'] = manifest.ref;
109144
proxyRes.headers['x-fileset-site'] = siteId;
145+
if (manifest.ttl) {
146+
proxyRes.headers['x-fileset-ttl'] = manifest.ttl;
147+
}
110148
res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);
111149
});
112150
});

src/upload.ts

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ const findUploadedFiles = async (manifest: Manifest, storageBucket: any) => {
4242
export async function uploadManifest(
4343
bucket: string,
4444
manifest: Manifest,
45-
force?: boolean
45+
force?: boolean,
46+
ttl?: Date
4647
) {
4748
bucket = bucket || DEFAULT_BUCKET; // If bucket is blank.
4849
console.log(`Using storage: ${bucket}/${getBlobPath(manifest.site, '')}`);
@@ -122,37 +123,61 @@ export async function uploadManifest(
122123

123124
// @ts-ignore
124125
bar.on('stop', () => {
125-
finalize(manifest);
126+
finalize(manifest, ttl);
126127
});
127128
} else {
128-
finalize(manifest);
129+
finalize(manifest, ttl);
129130
}
130131
}
131132

132-
function createEntity(key: entity.Key, manifest: Manifest) {
133+
async function createEntity(key: entity.Key, manifest: Manifest, ttl?: Date) {
134+
const manifestPaths = manifest.toJSON();
135+
let props: any = {
136+
created: new Date(),
137+
site: manifest.site,
138+
ref: manifest.ref,
139+
branch: manifest.branch,
140+
paths: manifestPaths,
141+
};
142+
if (ttl) {
143+
const key = datastore.key([
144+
'Fileset2Manifest',
145+
`branch:${manifest.branch}`,
146+
]);
147+
const resp = await datastore.get(key);
148+
if (resp && resp[0]) {
149+
console.log(
150+
`Adding TTL for branch ${manifest.branch} -> ${manifest.shortSha} @ ${ttl}`
151+
);
152+
const entity = resp[0];
153+
if (entity.ttls) {
154+
entity.ttls[ttl.toString()] = props;
155+
} else {
156+
entity.ttls = {ttl: props};
157+
}
158+
props = entity;
159+
}
160+
}
133161
return {
134162
key: key,
135-
excludeFromIndexes: ['paths'],
136-
data: {
137-
created: new Date(),
138-
ref: manifest.ref,
139-
branch: manifest.branch,
140-
paths: manifest.toJSON(),
141-
},
163+
excludeFromIndexes: ['paths', 'ttls'],
164+
data: props,
142165
};
143166
}
144167

145-
async function finalize(manifest: Manifest) {
168+
async function finalize(manifest: Manifest, ttl?: Date) {
169+
// Create shortSha mapping.
146170
const key = datastore.key(['Fileset2Manifest', manifest.shortSha]);
147-
const ent = createEntity(key, manifest);
171+
const ent = await createEntity(key, manifest);
148172
await datastore.save(ent);
149173

174+
// Create branch mapping.
150175
if (manifest.branch) {
151176
const branchKey = datastore.key([
152177
'Fileset2Manifest',
153178
`branch:${manifest.branch}`,
154179
]);
155-
const branchEnt = createEntity(branchKey, manifest);
180+
const branchEnt = await createEntity(branchKey, manifest, ttl);
156181
await datastore.save(branchEnt);
157182
}
158183

0 commit comments

Comments
 (0)