async function deployStaticFiles({
app_id,
immediate = false,
files = [
]
} = {}) {
const id = files[0].target;
if (spinners[id]) return html`${spinners[id]}`;
if (!(viewof user).value.uid) return html`${viewof user}`;
if (!session.access_token) {
"deployStaticFiles: no access token"
if (nextNonce !== session.nonce) {
await firebase.firestore()
.doc(`/services/netlify-proxy/sessions/${(viewof user).value.uid}`)
.set({
nonce: nextNonce
}, {merge: true});
}
async function authorize(evt) {
const url = 'https://app.netlify.com/authorize?' +
'client_id=' + client_id +
'&response_type=token' +
'&redirect_uri=https://observablehq.com/@endpointservices/netlify' +
'&state=' + encodeURIComponent(JSON.stringify(session));
evt.target.href = url;
}
return html`
<a class="button"
href="https://app.netlify.com/authorize"
onclick=${authorize}>authorize with Netlify</a>
`;
}
// have access token
const subdomain = location.host.split(".")[0];
async function digestFileAndDeploySite(evt) {
const unitsPromise = files.map(async file => {
const unit = ({
...file,
source: file.source,
tags: file.tags || [],
dependsOnTags: file.dependsOnTags || [],
app_id,
target: file.target,
type: "file",
safeTarget: encodeURIComponent(file.target),
});
const unitURI = `/services/netlify-proxy/subdomains/${subdomain}/apps/${app_id}/units/${unit.safeTarget}`;
console.log("deployStaticFile: fetching previous deploy");
const existing = (await firebase.firestore().doc(unitURI).get()).data()
if (existing && existing.creationDate === undefined) {
console.log("deployStaticFile: backfilling creationDate");
await firebase.firestore()
.doc(`/services/netlify-proxy/subdomains/${subdomain}/apps/${app_id}/units/${unit.safeTarget}`)
.set({creationDate: firebase.firebase_.firestore.FieldValue.serverTimestamp()}, {merge: true})
}
console.log("deployStaticFile: syncing paramaters");
await firebase.firestore()
.doc(`/services/netlify-proxy/subdomains/${subdomain}/apps/${app_id}/units/${unit.safeTarget}`)
.set({
...(!existing && {
creationDate: firebase.firebase_.firestore.FieldValue.serverTimestamp()
}),
...unit
}, {merge: true})
return unit;
});
var units = await promiseRecursive(unitsPromise);
var tags = units.reduce(
(list, unit) => list.concat(unit.tags)
,[]
)
const cache = {};
message(id, "Deploying...\nQuerying dependents");
// For now we do one round of dependencies
const dependees = tags.length > 0 ? (await firebase.firestore()
.collection(`/services/netlify-proxy/subdomains/${subdomain}/apps/${app_id}/units`)
.where("type", "==", "file")
.where("dependsOnTags", "array-contains-any", [...new Set(tags || [])])
.limit(100)
.get()).docs.map(d => d.data())
: [];
const updates = dependees.concat(units).map(unit => updateDigest({
app_id, subdomain
}, unit, cache));
message(id, `${updates.length} files to update`)
await Promise.all(updates);
message(id, `Retreiving existing ${dependees.length} files metadata`)
const existingFiles = (await firebase.firestore()
.collection(`/services/netlify-proxy/subdomains/${subdomain}/apps/${app_id}/units`)
.where("type", "==", "file")
.get()).docs.map(d => d.data());
// console.log(existingFiles, cache)
message(id, `Creating deploy with Netlify`)
const deploy_json = await createDeploy(app_id, existingFiles);
const requiredDigests = deploy_json.required;
deployments[id] = deploy_json.ssl_url;
message(id, `Syncing required files ${requiredDigests}`)
const uploads = requiredDigests.map(digest => {
if (!cache[digest]) {
console.error(`Netlify has requested digest ${digest} which was not part of our sync`);
// So lets find it
let record;
firebase.firestore()
.collection(`/services/netlify-proxy/subdomains/${subdomain}/apps/${app_id}/units`)
.where("digest", "==", digest)
.get()
.then(snap => {
record = snap.docs[0].data()
return fetch(record.source);
}).then(response => response.text())
.then(content => {
return uploadContent(deploy_json.id, record.target, content);
})
} else {
return uploadContent(deploy_json.id, cache[digest].target, cache[digest].content);
}
});
try {
await Promise.all(uploads);
message(id, null);
} catch (err) {
message(id, err.message);
}
}
if (immediate && (!deployments[id])) {
deployments[id] = 'inprogress'
digestFileAndDeploySite()
} else if (deployments[id] === 'inprogress') {
return md`Deployment in progress`
} else {
return html`
${Inputs.table(files)}
${deployments[id] ?
html`<p>Deployed to <a target="_blank" href=${deployments[id] + files[0].target}>${deployments[id] + files[0].target}</a>`
: null}
<button class="button" onclick=${digestFileAndDeploySite}>deploy</button>
`
}
}