-
Notifications
You must be signed in to change notification settings - Fork 895
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6470299
commit cd6d61a
Showing
1 changed file
with
70 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,85 @@ | ||
import { writeFile } from "fs/promises"; | ||
|
||
let firebaseConfig = {}; | ||
if (process.env.FIREBASE_CONFIG?.startsWith("{")) { | ||
// TODO probably want a more robust yaml parse | ||
firebaseConfig = Object.fromEntries(process.env.FIREBASE_CONFIG.match(/[^(\:\{\},)]+\:[^(,})]+/g).map(it => { | ||
const parts = it.split(":"); | ||
return [parts[0], parts.slice(1).join(":")] | ||
})); | ||
} | ||
import { writeFile, readFile } from "node:fs/promises"; | ||
import { pathToFileURL } from "node:url"; | ||
import { isAbsolute, join } from "node:path"; | ||
|
||
const projectId = firebaseConfig.projectId; | ||
const appId = firebaseConfig.appId; | ||
const apiKey = firebaseConfig.apiKey; | ||
async function getWebConfig() { | ||
let configFromEnvironment = undefined; | ||
// $FIREBASE_WEBAPP_CONFIG can be either a JSON representation of FirebaseOptions or the path | ||
// to a filename | ||
if (process.env.FIREBASE_WEBAPP_CONFIG) { | ||
if (process.env.FIREBASE_WEBAPP_CONFIG.startsWith("{")) { | ||
try { | ||
configFromEnvironment = JSON.parse(process.env.FIREBASE_WEBAPP_CONFIG); | ||
} catch(e) { | ||
console.error("FIREBASE_WEBAPP_CONFIG could not be parsed.", e); | ||
} | ||
} else { | ||
const fileName = process.env.FIREBASE_WEBAPP_CONFIG; | ||
const fileURL = pathToFileURL(isAbsolute(fileName) ? fileName : join(process.cwd(), fileName)); | ||
const fileContents = await readFile(fileURL, "utf-8").catch((err) => { | ||
console.error(err); | ||
return undefined; | ||
}); | ||
if (fileContents) { | ||
try { | ||
configFromEnvironment = JSON.parse(fileContents); | ||
} catch(e) { | ||
console.error(`Contents of ${fileName} could not be parsed.`, e); | ||
} | ||
} | ||
} | ||
} | ||
|
||
const config = projectId && appId && apiKey && await (await fetch( | ||
`https://firebase.googleapis.com/v1alpha/projects/${projectId}/apps/${appId}/webConfig`, | ||
{ headers: { "x-goog-api-key": apiKey } } | ||
)).json(); | ||
// In Firebase App Hosting the config provided to the environment variable is up-to-date and | ||
// "complete" we should not reach out to the webConfig endpoint to freshen it | ||
if (process.env.X_GOOGLE_TARGET_PLATFORM === "fah") { | ||
return configFromEnvironment; | ||
} | ||
|
||
if (config) { | ||
config.apiKey = apiKey; | ||
if (!configFromEnvironment) { | ||
return undefined; | ||
} | ||
const projectId = configFromEnvironment.projectId || "-"; | ||
const appId = configFromEnvironment.appId; | ||
const apiKey = configFromEnvironment.apiKey; | ||
if (!appId || !apiKey) { | ||
console.error("appId and apiKey are needed"); | ||
return undefined; | ||
} | ||
const response = await fetch( | ||
`https://firebase.googleapis.com/v1alpha/projects/${projectId}/apps/${appId}/webConfig`, | ||
{ headers: { "x-goog-api-key": apiKey } } | ||
); | ||
if (!response.ok) { | ||
// TODO add sensible error | ||
console.error("yikes."); | ||
return undefined; | ||
} | ||
const json = await response.json().catch(() => { | ||
// TODO add sensible error | ||
console.error("also yikes."); | ||
return undefined; | ||
}); | ||
return { ...json, apiKey }; | ||
} | ||
|
||
let emulatorHosts = { | ||
const config = await getWebConfig(); | ||
|
||
const emulatorHosts = { | ||
// TODO: remote config, functions, and data connect emulators? | ||
firestore: process.env.FIRESTORE_EMULATOR_HOST, | ||
database: process.env.FIREBASE_DATABASE_EMULATOR_HOST, | ||
storage: process.env.FIREBASE_STORAGE_EMULATOR_HOST, | ||
auth: process.env.FIREBASE_AUTH_EMULATOR_HOST, | ||
}; | ||
|
||
if (!Object.values(emulatorHosts).filter(it => it).length) { | ||
emulatorHosts = undefined; | ||
} | ||
const anyEmulatorHosts = Object.values(emulatorHosts).filter(it => it).length > 0; | ||
|
||
const defaults = (config || emulatorHosts) && { config, emulatorHosts }; | ||
// getDefaults() will use this object, rather than fallback to other autoinit suppliers, if it's | ||
// truthy—if we've done nothing here, make it falsy. | ||
const defaults = (config || anyEmulatorHosts) ? { config, emulatorHosts } : undefined; | ||
|
||
await Promise.all([ | ||
writeFile("./defaults.js", `module.exports = ${JSON.stringify(defaults)}`), | ||
writeFile("./defaults.mjs", `export default ${JSON.stringify(defaults)}`), | ||
writeFile(join(import.meta.dirname, "defaults.js"), `module.exports = ${JSON.stringify(defaults)}`), | ||
writeFile(join(import.meta.dirname, "defaults.mjs"), `export default ${JSON.stringify(defaults)}`), | ||
]); |