Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
// @ts-ignore incorrect typescript typings
import * as Mailchimp from "mailchimp-api-v3";
import config from "./config";
import * as logs from "./logs";
logs.init();
let mailchimp: Mailchimp;
try {
mailchimp = new Mailchimp(config.mailchimpApiKey);
} catch (err) {
logs.initError(err);
}
export const addUserToList = functions.handler.auth.user.onCreate(
async (user): Promise => {
logs.start();
if (!mailchimp) {
logs.mailchimpNotInitialized();
return;
}
const { email, uid } = user;
if (!email) {
logs.userNoEmail();
return;
}
try {
logs.userAdding(uid, config.mailchimpAudienceId);
const admin = require("firebase-admin");
const functions = require("firebase-functions");
const bitly_1 = require("bitly");
const config_1 = require("./config");
const logs = require("./logs");
var ChangeType;
(function (ChangeType) {
ChangeType[ChangeType["CREATE"] = 0] = "CREATE";
ChangeType[ChangeType["DELETE"] = 1] = "DELETE";
ChangeType[ChangeType["UPDATE"] = 2] = "UPDATE";
})(ChangeType || (ChangeType = {}));
const bitly = new bitly_1.BitlyClient(config_1.default.bitlyAccessToken);
// Initialize the Firebase Admin SDK
admin.initializeApp();
logs.init();
exports.rtdburlshortener = functions.handler.database.ref.onWrite((change) => __awaiter(this, void 0, void 0, function* () {
logs.start();
if (config_1.default.urlFieldName === config_1.default.shortUrlFieldName) {
logs.fieldNamesNotDifferent();
return;
}
const changeType = getChangeType(change);
switch (changeType) {
case ChangeType.CREATE:
yield handleCreateDocument(change.after);
break;
case ChangeType.DELETE:
handleDeleteDocument();
break;
case ChangeType.UPDATE:
yield handleUpdateDocument(change.before, change.after);
break;
Object.defineProperty(exports, "__esModule", { value: true });
const crypto = require("crypto");
const functions = require("firebase-functions");
// @ts-ignore incorrect typescript typings
const Mailchimp = require("mailchimp-api-v3");
const config_1 = require("./config");
const logs = require("./logs");
logs.init();
let mailchimp;
try {
mailchimp = new Mailchimp(config_1.default.mailchimpApiKey);
}
catch (err) {
logs.initError(err);
}
exports.addUserToList = functions.handler.auth.user.onCreate((user) => __awaiter(this, void 0, void 0, function* () {
logs.start();
if (!mailchimp) {
logs.mailchimpNotInitialized();
return;
}
const { email, uid } = user;
if (!email) {
logs.userNoEmail();
return;
}
try {
logs.userAdding(uid, config_1.default.mailchimpAudienceId);
const results = yield mailchimp.post(`/lists/${config_1.default.mailchimpAudienceId}/members`, {
email_address: email,
status: "subscribed",
});
const functions = require("firebase-functions");
const translate_1 = require("@google-cloud/translate");
const config_1 = require("./config");
const logs = require("./logs");
const validators = require("./validators");
var ChangeType;
(function (ChangeType) {
ChangeType[ChangeType["CREATE"] = 0] = "CREATE";
ChangeType[ChangeType["DELETE"] = 1] = "DELETE";
ChangeType[ChangeType["UPDATE"] = 2] = "UPDATE";
})(ChangeType || (ChangeType = {}));
const translate = new translate_1.Translate({ projectId: process.env.PROJECT_ID });
// Initialize the Firebase Admin SDK
admin.initializeApp();
logs.init();
exports.fstranslate = functions.handler.firestore.document.onWrite((change) => __awaiter(this, void 0, void 0, function* () {
logs.start();
const { languages, inputFieldName, outputFieldName } = config_1.default;
if (validators.fieldNamesMatch(inputFieldName, outputFieldName)) {
logs.fieldNamesNotDifferent();
return;
}
if (validators.fieldNameIsTranslationPath(inputFieldName, outputFieldName, languages)) {
logs.inputFieldNameIsOutputPath();
return;
}
const changeType = getChangeType(change);
try {
switch (changeType) {
case ChangeType.CREATE:
yield handleCreateDocument(change.after);
break;
case "RETRY":
// Wrapping in transaction to allow for automatic retries (#48)
await admin.firestore().runTransaction((transaction) => {
transaction.update(change.after.ref, {
"delivery.state": "PROCESSING",
"delivery.leaseExpireTime": admin.firestore.Timestamp.fromMillis(
Date.now() + 60000
),
});
return Promise.resolve();
});
return deliver(payload, change.after.ref);
}
}
export const processQueue = functions.handler.firestore.document.onWrite(
async (change) => {
initialize();
logs.start();
try {
await processWrite(change);
} catch (err) {
logs.error(err);
return null;
}
logs.complete();
}
);
// Throw error here to bypass success logging
throw new Error(`Invalid change type: ${changeType}`);
}
logs.success();
}
catch (err) {
logs.error(err); // These caught errors will not cause Cloud Functions to retry
}
}));
/**
* Use pessimistic transactions to clean up old tombstones whose timestamp is older
* than TIME_THRESHOLD and is not currently online.
*
* @param userID: reference
*/
exports.cleanUpDeadSessions = functions.handler.pubsub.topic.onPublish(() => __awaiter(void 0, void 0, void 0, function* () {
logs.startCleanup();
if (config_1.default.firestore_path === undefined) {
throw new Error('Undefined firestore path. Please re-install and reconfigure the Firestore collection.');
}
const docRefArr = yield admin.firestore().collection(config_1.default.firestore_path).listDocuments();
const currentTime = (new Date).getTime();
for (const docRef of docRefArr) {
// Run pessimistic transaction on each user document to remove tombstones
logs.currentDocument(docRef.id);
yield admin.firestore().runTransaction((transaction) => __awaiter(void 0, void 0, void 0, function* () {
yield transaction.get(docRef).then((doc) => __awaiter(void 0, void 0, void 0, function* () {
const docData = doc.data();
// Read tombstone data if available
if (docData !== undefined && docData["last_updated"] instanceof Object) {
// For each tombstone, determine which are old enough to delete (specified by TIME_THRESHOLD)
const updateArr = {};
* limitations under the License.
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const functions = require("firebase-functions");
const config_1 = require("./config");
const logs = require("./logs");
logs.init();
exports.rtdblimit = functions.handler.database.ref.onCreate((snapshot) => __awaiter(this, void 0, void 0, function* () {
logs.start();
try {
const parentRef = snapshot.ref.parent;
const parentSnapshot = yield parentRef.once("value");
logs.childCount(parentRef.path, parentSnapshot.numChildren());
if (parentSnapshot.numChildren() > config_1.default.maxCount) {
let childCount = 0;
const updates = {};
parentSnapshot.forEach((child) => {
if (++childCount <= parentSnapshot.numChildren() - config_1.default.maxCount) {
updates[child.key] = null;
}
});
logs.pathTruncating(parentRef.path, config_1.default.maxCount);
yield parentRef.update(updates);
logs.pathTruncated(parentRef.path, config_1.default.maxCount);
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as functions from "firebase-functions";
import fetch from "node-fetch";
import config from "./config";
import * as logs from "./logs";
logs.init();
export const slackMessenger = functions.handler.pubsub.topic.onPublish(
async (message): Promise => {
logs.start();
try {
const { text } = message.json;
if (!text) {
logs.textMissing();
return;
}
logs.messageSending(config.slackWebhookUrl);
await fetch(config.slackWebhookUrl, {
method: "POST",
body: JSON.stringify({ text }),
headers: {
"Content-Type": "application/json",
const url = this.extractUrl(snapshot);
logs.shortenUrl(url);
try {
const response = yield this.bitly.shorten(url);
const { url: shortUrl } = response;
logs.shortenUrlComplete(shortUrl);
yield this.updateShortUrl(snapshot, shortUrl);
}
catch (err) {
logs.error(err);
}
});
}
}
const urlShortener = new FirestoreBitlyUrlShortener(config_1.default.urlFieldName, config_1.default.shortUrlFieldName, config_1.default.bitlyAccessToken);
exports.fsurlshortener = functions.handler.firestore.document.onWrite((change) => __awaiter(this, void 0, void 0, function* () {
return urlShortener.onDocumentWrite(change);
}));
admin.initializeApp();
const firestore = admin.firestore();
firestore.settings({ timestampsInSnapshots: true });
let pubsub;
const SHARDS_COLLECTION_ID = "_counter_shards_";
const WORKERS_COLLECTION_ID = "_counter_workers_";
/**
* The controllerCore is scheduled every minute. It tries to aggregate shards if
* there's less than 200 of them. Otherwise it is scheduling and monitoring
* workers to do the aggregation.
*/
export const controllerCore = functions.handler.pubsub.topic.onPublish(
async () => {
const metadocRef = firestore.doc(process.env.INTERNAL_STATE_PATH);
const controller = new ShardedCounterController(
metadocRef,
SHARDS_COLLECTION_ID
);
let status = await controller.aggregateOnce({ start: "", end: "" }, 200);
if (
status === ControllerStatus.WORKERS_RUNNING ||
status === ControllerStatus.TOO_MANY_SHARDS ||
status === ControllerStatus.FAILURE
) {
await controller.rescheduleWorkers();
}
return null;
}