diff --git a/app/common/enums.js b/app/common/enums.js index 8b8c157..3f3ab4e 100644 --- a/app/common/enums.js +++ b/app/common/enums.js @@ -304,6 +304,7 @@ const AD_AGENCY = { RENTAL: "RENTAL", PROSTOR: "PROSTOR", AKTIDO: "AKTIDO", + KIVI: "KIVI", SALJIC: "SALJIC" }; diff --git a/app/common/publishEnums.js b/app/common/publishEnums.js new file mode 100644 index 0000000..e98f361 --- /dev/null +++ b/app/common/publishEnums.js @@ -0,0 +1,492 @@ +const { + AD_CATEGORY, + ACCESS_ROAD_TYPE, + HEATING_TYPE, + FURNISHING_TYPE +} = require("./enums"); + +const BASIC_BOOLEAN_PUBLISH = [ + { + dbField: "newBuilding", + title: "Novogradnja", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.GARAGE + ] + }, + { + dbField: "balcony", + title: "Balkon", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE + ] + }, + + { + dbField: "elevator", + title: "Lift", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.OFFICE + ] + }, + { + dbField: "recentlyAdapted", + title: "Nedavno adaptirano", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE + ] + } +]; + +const BASIC_INPUT_PUBLISH = [ + { + dbField: "title", + title: "Naslov", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.LAND, + AD_CATEGORY.GARAGE + ], + constraint: ["required"] + }, + { + dbField: "shortDescription", + title: "Opis", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.LAND, + AD_CATEGORY.GARAGE + ], + constraint: [] + }, + { + dbField: "price", + title: "Cijena (KM)", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.LAND, + AD_CATEGORY.GARAGE + ], + constraint: ["numerical"] + }, + { + dbField: "area", + title: "Površina (m\xB2)", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.LAND, + AD_CATEGORY.GARAGE + ], + constraint: ["numerical"] + }, + { + dbField: "gardenSize", + title: "Površina okućnice (m\xB2)", + categoriesToShow: [AD_CATEGORY.HOUSE, AD_CATEGORY.COTTAGE], + constraint: ["numerical"] + }, + { + dbField: "streetName", + title: "Adresa", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.LAND, + AD_CATEGORY.GARAGE + ], + constraint: [] + }, + { + dbField: "numberOfRooms", + title: "Broj soba", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE + ], + constraint: ["integer"] + }, + { + dbField: "numberOfFloors", + title: "Broj spratova", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE + ], + constraint: ["integer"] + }, + { + dbField: "floor", + title: "Sprat", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.OFFICE + ], + constraint: ["integer"] + } +]; + +const BASIC_SEGMENT_PUBLISH = [ + { + dbField: "furnishingType", + title: "Namještaj", + values: Object.keys(FURNISHING_TYPE).map(key => FURNISHING_TYPE[key]), + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE + ] + } +]; + +const ADDITIONAL_BOOLEAN_PUBLISH = [ + { + dbField: "water", + title: "Voda", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE + ] + }, + { + dbField: "electricity", + title: "Struja", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.GARAGE + ] + }, + { + dbField: "drainageSystem", + title: "Kanalizacija", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE + ] + }, + { + dbField: "registeredInZkBooks", + title: "Uknjiženo", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.LAND, + AD_CATEGORY.GARAGE + ] + }, + + { + dbField: "parking", + title: "Parking", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE + ] + }, + { + dbField: "garage", + title: "Garaža", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE + ] + }, + { + dbField: "gas", + title: "Plin", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE + ] + }, + { + dbField: "antiTheftDoor", + title: "Blindirana vrata", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE + ] + }, + { + dbField: "airCondition", + title: "Klimatizirano", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE + ] + }, + { + dbField: "phoneConnection", + title: "Telefon", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE + ] + }, + { + dbField: "cableTV", + title: "Kablovska", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE + ] + }, + { + dbField: "internet", + title: "Internet", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE + ] + }, + { + dbField: "basementAttic", + title: "Podrum-Tavan", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE + ] + }, + { + dbField: "storeRoom", + title: "Ostava", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE + ] + }, + { + dbField: "videoSurveillance", + title: "Video nadzor", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.GARAGE + ] + }, + { + dbField: "alarm", + title: "Alarm", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.GARAGE + ] + }, + { + dbField: "suitableForStudents", + title: "Za studente", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE + ] + }, + { + dbField: "includingBills", + title: "Uključen trošak režija", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.GARAGE + ] + }, + { + dbField: "animalsAllowed", + title: "Kućni ljubimci dozvoljeni", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE + ] + }, + { + dbField: "pool", + title: "Bazen", + categoriesToShow: [AD_CATEGORY.HOUSE, AD_CATEGORY.COTTAGE] + }, + { + dbField: "exchange", + title: "Zamjena", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.LAND, + AD_CATEGORY.GARAGE + ] + }, + { + dbField: "urbanPlanPermit", + title: "Urbanistička dozvola", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.LAND, + AD_CATEGORY.GARAGE + ] + }, + { + dbField: "buildingPermit", + title: "Građevinska dozvola", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.LAND, + AD_CATEGORY.GARAGE + ] + } +]; + +const ADDITIONAL_INPUT_PUBLISH = [ + { + dbField: "longDescription", + title: "Detaljan opis", + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.LAND, + AD_CATEGORY.GARAGE + ] + } +]; + +const ADDITIONAL_SEGMENT_PUBLISH = [ + { + dbField: "accessRoadType", + title: "Pristupni put", + values: Object.keys(ACCESS_ROAD_TYPE).map(key => ACCESS_ROAD_TYPE[key]), + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE, + AD_CATEGORY.LAND, + AD_CATEGORY.GARAGE + ] + }, + { + dbField: "heatingType", + title: "Grijanje", + values: Object.keys(HEATING_TYPE).map(key => HEATING_TYPE[key]), + categoriesToShow: [ + AD_CATEGORY.FLAT, + AD_CATEGORY.HOUSE, + AD_CATEGORY.APARTMENT, + AD_CATEGORY.COTTAGE, + AD_CATEGORY.OFFICE + ] + } +]; + +module.exports = { + BASIC_INPUT_PUBLISH, + BASIC_SEGMENT_PUBLISH, + BASIC_BOOLEAN_PUBLISH, + ADDITIONAL_BOOLEAN_PUBLISH, + ADDITIONAL_INPUT_PUBLISH, + ADDITIONAL_SEGMENT_PUBLISH +}; diff --git a/app/controllers/publishRealEstate.js b/app/controllers/publishRealEstate.js new file mode 100644 index 0000000..4686cd8 --- /dev/null +++ b/app/controllers/publishRealEstate.js @@ -0,0 +1,319 @@ +const { findRealEstateByAgencyId } = require("../helpers/db/realEstate"); +const { bulkUpsertKiviPhotos } = require("../helpers/db/kiviOriginalAdsPhotos"); +const { currentKiviRealEstate } = require("../helpers/url"); + +const validate = require("validate.js"); + +const { + AD_CATEGORY, + FURNISHING_TYPE, + ACCESS_ROAD_TYPE, + HEATING_TYPE +} = require("../common/enums"); +const { + BASIC_BOOLEAN_PUBLISH, + BASIC_SEGMENT_PUBLISH, + ADDITIONAL_BOOLEAN_PUBLISH, + ADDITIONAL_SEGMENT_PUBLISH, + BASIC_INPUT_PUBLISH, + ADDITIONAL_INPUT_PUBLISH +} = require("../common/publishEnums"); + +const getPublishInputs = async (req, res) => { + const kiviOriginal = await currentKiviRealEstate(req); + + const realEstate = await findRealEstateByAgencyId(kiviOriginal.kiviAdId); + + if (!realEstate || !realEstate.dataValues) { + res.render("notFound", { title: " " }); + return; + } + + const pageTitle = "Podaci o nekretnini"; + + const { + price, + area, + adType, + realEstateType, + locationLat, + locationLong, + accessRoadType, + heatingType, + balcony, + newBuilding, + elevator, + recentlyAdapted, + gardenSize, + numberOfRooms, + numberOfFloors, + floor, + water, + electricity, + drainageSystem, + registeredInZkBooks, + parking, + garage, + gas, + antiTheftDoor, + airCondition, + phoneConnection, + cableTV, + internet, + basementAttic, + storeRoom, + videoSurveillance, + alarm, + suitableForStudents, + includingBills, + animalsAllowed, + pool, + exchange, + urbanPlanPermit, + buildingPermit, + furnishingType, + shortDescription, + streetName, + title, + longDescription + } = realEstate; + const category = AD_CATEGORY[realEstateType] || AD_CATEGORY.FLAT; + + // TODO: Maybe this is slow, pay attention to this + const filterInputs = filterObject => { + const filterCategories = filterObject.categoriesToShow; + return filterCategories.indexOf(category) !== -1; + }; + //Boolean inputs to be shown on Basic Data tab + const basicBooleanPublishInputs = BASIC_BOOLEAN_PUBLISH.filter(filterInputs); + const basicBooleanPublishValues = { + balcony, + elevator, + newBuilding, + recentlyAdapted + }; + //Boolean inputs to be shown on Additional Data tab + const additionalBooleanPublishInputs = ADDITIONAL_BOOLEAN_PUBLISH.filter( + filterInputs + ); + const additionalBooleanPublishValues = { + water, + electricity, + drainageSystem, + registeredInZkBooks, + parking, + garage, + gas, + antiTheftDoor, + airCondition, + phoneConnection, + cableTV, + internet, + basementAttic, + storeRoom, + videoSurveillance, + alarm, + suitableForStudents, + includingBills, + animalsAllowed, + pool, + exchange, + urbanPlanPermit, + buildingPermit + }; + //Segment select inputs to be shown on Basic Data tab + const basicSegmentSelectInputs = BASIC_SEGMENT_PUBLISH.filter(filterInputs); + const basicSegmentSelectValues = { + furnishingType + }; + //Segment select inputs to be shown on Additional Data tab + const additionalSegmentSelectInputs = ADDITIONAL_SEGMENT_PUBLISH.filter( + filterInputs + ); + const additionalSegmentSelectValues = { + accessRoadType, + heatingType + }; + //Input text type inputs to be shown on Basic Data tab + const basicInputInputs = BASIC_INPUT_PUBLISH.filter(filterInputs); + const basicInputValues = { + price, + area, + gardenSize, + numberOfRooms, + numberOfFloors, + floor, + title, + shortDescription, + streetName + }; + //Input type textare to be shown on Additional Data + const additionalInputInputs = ADDITIONAL_INPUT_PUBLISH.filter(filterInputs); + const additionalInputValues = { + longDescription + }; + + res.render("publishRealEstate", { + title: pageTitle, + basicBooleanPublishInputs, + basicBooleanPublishValues, + additionalBooleanPublishInputs, + additionalBooleanPublishValues, + basicSegmentSelectInputs, + basicSegmentSelectValues, + additionalSegmentSelectInputs, + additionalSegmentSelectValues, + basicInputInputs, + basicInputValues, + additionalInputInputs, + additionalInputValues, + validate: validate + }); +}; + +const postPublishInputs = async (req, res) => { + const kiviOriginal = await currentKiviRealEstate(req); + + const realEstate = await findRealEstateByAgencyId(kiviOriginal.kiviAdId); + + if (!realEstate || !realEstate.dataValues) { + res.render("notFound", { title: " " }); + return; + } + + const nextStepPage = req.query.nextStep || "/uspjesnaobjava"; + + //Request body + //console.log("Body:", req.body); + + const balcony = req.body.balcony === "on"; + const elevator = req.body.elevator === "on"; + const newBuilding = req.body.newBuilding === "on"; + const recentlyAdapted = req.body.recentlyAdapted === "on"; + const water = req.body.water === "on"; + const electricity = req.body.electricity === "on"; + const drainageSystem = req.body.drainageSystem === "on"; + const registeredInZkBooks = req.body.registeredInZkBooks === "on"; + const parking = req.body.parking === "on"; + const garage = req.body.garage === "on"; + const gas = req.body.gas === "on"; + const antiTheftDoor = req.body.antiTheftDoor === "on"; + const airCondition = req.body.airCondition === "on"; + const phoneConnection = req.body.phoneConnection === "on"; + const cableTV = req.body.cableTV === "on"; + const internet = req.body.internet === "on"; + const basementAttic = req.body.basementAttic === "on"; + const storeRoom = req.body.storeRoom === "on"; + const videoSurveillance = req.body.videoSurveillance === "on"; + const alarm = req.body.alarm === "on"; + const suitableForStudents = req.body.suitableForStudents === "on"; + const includingBills = req.body.includingBills === "on"; + const animalsAllowed = req.body.animalsAllowed === "on"; + const pool = req.body.pool === "on"; + const exchange = req.body.exchange === "on"; + const urbanPlanPermit = req.body.urbanPlanPermit === "on"; + const buildingPermit = req.body.buildingPermit === "on"; + + const furnishingType = req.body.furnishingType; + //VALIDACIJA TAKO POTVRDITI DA JE ISPRAVNA VRIJEDNOST + /* if (!FURNISHING_TYPE[furnishingType]) { + res.render("notFound", { title: " Greška !" }); + return; + } */ + const accessRoadType = req.body.accessRoadType; + /*if (!ACCESS_ROAD_TYPE[accessRoadType]) { + res.render("notFound", { title: " Greška !" }); + return; + } */ + const heatingType = req.body.heatingType; + /*if (!HEATING_TYPE[heatingType]) { + res.render("notFound", { title: " Greška !" }); + return; + }*/ + + const price = parseFloat(req.body.price) || null; + const area = parseFloat(req.body.area) || null; + const gardenSize = parseFloat(req.body.gardenSize) || null; + const numberOfRooms = parseInt(req.body.numberOfRooms) || null; + const numberOfFloors = parseInt(req.body.numberOfFloors) || null; + const floor = parseInt(req.body.floor) || null; + const title = req.body.title || ""; + const shortDescription = req.body.shortDescription || ""; + const streetName = req.body.streetName || ""; + const longDescription = req.body.longDescription || ""; + + const locationLat = req.body.lat || null; + const locationLong = req.body.lng || null; + //Contact email saved in other table + const contactEmail = req.body.email || ""; + //Image urls are stored in new table + const imageUrls = + req.body.imageUrls.split("|").filter(url => url !== "") || []; + const imageUrlsData = imageUrls.map(url => { + return { + kiviAdId: kiviOriginal.kiviAdId, + photoUrl: url + }; + }); + const savedImageUrls = await bulkUpsertKiviPhotos(imageUrlsData); + + realEstate.balcony = balcony; + realEstate.elevator = elevator; + realEstate.newBuilding = newBuilding; + realEstate.recentlyAdapted = recentlyAdapted; + realEstate.water = water; + realEstate.electricity = electricity; + realEstate.drainageSystem = drainageSystem; + realEstate.registeredInZkBooks = registeredInZkBooks; + realEstate.parking = parking; + realEstate.garage = garage; + realEstate.gas = gas; + realEstate.antiTheftDoor = antiTheftDoor; + realEstate.airCondition = airCondition; + realEstate.phoneConnection = phoneConnection; + realEstate.cableTV = cableTV; + realEstate.internet = internet; + realEstate.basementAttic = basementAttic; + realEstate.storeRoom = storeRoom; + realEstate.videoSurveillance = videoSurveillance; + realEstate.alarm = alarm; + realEstate.suitableForStudents = suitableForStudents; + realEstate.includingBills = includingBills; + realEstate.animalsAllowed = animalsAllowed; + realEstate.pool = pool; + realEstate.exchange = exchange; + realEstate.urbanPlanPermit = urbanPlanPermit; + realEstate.buildingPermit = buildingPermit; + + realEstate.furnishingType = furnishingType; + realEstate.accessRoadType = accessRoadType; + realEstate.heatingType = heatingType; + + realEstate.price = price; + realEstate.area = area; + realEstate.gardenSize = gardenSize; + realEstate.numberOfRooms = numberOfRooms; + realEstate.numberOfFloors = numberOfFloors; + realEstate.floor = floor; + realEstate.title = title; + realEstate.shortDescription = shortDescription; + realEstate.streetName = streetName; + + realEstate.longDescription = longDescription; + + realEstate.locationLat = locationLat; + realEstate.locationLong = locationLong; + + kiviOriginal.email = contactEmail; + + await realEstate.save(); + + await kiviOriginal.save(); + + res.redirect(nextStepPage); +}; + +module.exports = { + getPublishInputs, + postPublishInputs +}; diff --git a/app/controllers/publishRealEstateTypes.js b/app/controllers/publishRealEstateTypes.js new file mode 100644 index 0000000..c053f1e --- /dev/null +++ b/app/controllers/publishRealEstateTypes.js @@ -0,0 +1,101 @@ +const { currentKiviRealEstate } = require("../helpers/url"); +const { + createRealEstate, + findRealEstateByAgencyId +} = require("../helpers/db/realEstate"); +const { createKiviOriginal } = require("../helpers/db/kiviOriginal"); + +const { AD_CATEGORY, AD_TYPE, AD_AGENCY } = require("../common/enums"); + +const getPublishTypes = async (req, res) => { + const kiviOriginal = await currentKiviRealEstate(req); + + const realEstate = await findRealEstateByAgencyId(kiviOriginal.kiviAdId); + + const title = "Koju nekretninu nudite?"; + let selectedAdType = AD_TYPE.AD_TYPE_SALE.id; + const labelAdType = ["Prodaj", "Iznajmi"]; + + if ( + realEstate && + realEstate.adType && + realEstate.adType === AD_TYPE.AD_TYPE_RENT.stringId + ) { + selectedAdType = AD_TYPE.AD_TYPE_RENT.id; + } + const realEstateTypes = Object.keys(AD_CATEGORY) + .map(category => AD_CATEGORY[category]) + .filter(category => category.title); + + res.render("realEstateType", { + selectedAdType, + labelAdType, + realEstateTypes, + title, + AD_TYPE + }); +}; + +const postPublishTypes = async (req, res) => { + const kiviOriginal = await currentKiviRealEstate(req); + + const realEstate = await findRealEstateByAgencyId(kiviOriginal.kiviAdId); + + const adType = parseInt(req.body.adType); + + const adTypeStringIds = { + [AD_TYPE.AD_TYPE_SALE.id]: AD_TYPE.AD_TYPE_SALE.stringId, + [AD_TYPE.AD_TYPE_RENT.id]: AD_TYPE.AD_TYPE_RENT.stringId + }; + + const adTypeStringId = + adTypeStringIds[adType] || AD_TYPE.AD_TYPE_SALE.stringId; + + const validRealEstateTypes = Object.keys(AD_CATEGORY).filter( + category => !!AD_CATEGORY[category].title + ); + + const selectedRealEstateType = req.body.realEstateType || null; + if (validRealEstateTypes.indexOf(selectedRealEstateType) === -1) { + res.render("notFound", { title: " " }); + return; + } + + const nextStepPage = req.query.nextStep || "podacionekretnini"; + + let nextStepUrl = ""; + if (kiviOriginal && kiviOriginal.kiviAdId && realEstate && realEstate.id) { + // + nextStepUrl = `/${nextStepPage}/${kiviOriginal.kiviAdId}`; + + realEstate.adType = adTypeStringId; + realEstate.realEstateType = selectedRealEstateType; + + await realEstate.save(); + } else { + try { + const newKiviOriginal = await createKiviOriginal({ + email: "" + }); + + const newRealEstate = await createRealEstate({ + adType: adTypeStringId, + realEstateType: selectedRealEstateType, + url: "http://localhost:5000/", + originAgencyName: AD_AGENCY.KIVI, + agencyObjectId: newKiviOriginal.kiviAdId + }); + + nextStepUrl = `/${nextStepPage}/${newKiviOriginal.kiviAdId}`; + } catch (error) { + console.log(error); + nextStepUrl = `/`; + } + } + res.redirect(nextStepUrl); +}; + +module.exports = { + getPublishTypes, + postPublishTypes +}; diff --git a/app/controllers/publishSuccess.js b/app/controllers/publishSuccess.js new file mode 100644 index 0000000..f444b9f --- /dev/null +++ b/app/controllers/publishSuccess.js @@ -0,0 +1,8 @@ +const publishSuccess = async (req, res) => { + const title = "Uspjeh!"; + res.render("publishSuccess", { title }); +}; + +module.exports = { + publishSuccess +}; diff --git a/app/controllers/realEstateFilters.js b/app/controllers/realEstateFilters.js index 252ac46..cff099f 100644 --- a/app/controllers/realEstateFilters.js +++ b/app/controllers/realEstateFilters.js @@ -122,6 +122,8 @@ const getFilters = async (req, res) => { }; const postFilters = async (req, res) => { + // + console.log("postFilters"); const searchRequest = await currentSearchRequest(req); if (!searchRequest || !searchRequest.dataValues) { diff --git a/app/controllers/realEstateTypes.js b/app/controllers/realEstateTypes.js index 8cc1638..bfc8a7f 100644 --- a/app/controllers/realEstateTypes.js +++ b/app/controllers/realEstateTypes.js @@ -8,6 +8,7 @@ const getRealEstateTypes = async (req, res) => { const title = "Koju nekretninu tražite?"; let selectedAdType = AD_TYPE.AD_TYPE_SALE.id; + const labelAdType = [AD_TYPE.AD_TYPE_SALE.title, AD_TYPE.AD_TYPE_RENT.title]; if ( searchRequest && searchRequest.adType && @@ -21,6 +22,7 @@ const getRealEstateTypes = async (req, res) => { res.render("realEstateType", { selectedAdType, + labelAdType, realEstateTypes, title, AD_TYPE diff --git a/app/controllers/welcome.js b/app/controllers/welcome.js index 3104d4e..11b42b5 100644 --- a/app/controllers/welcome.js +++ b/app/controllers/welcome.js @@ -1,6 +1,7 @@ const { createSearchRequest } = require("../helpers/db/searchRequest"); - -const { AD_TYPE, AD_CATEGORY } = require("../common/enums"); +const { createRealEstate } = require("../helpers/db/realEstate"); +const { createKiviOriginal } = require("../helpers/db/kiviOriginal"); +const { AD_TYPE, AD_CATEGORY, AD_AGENCY } = require("../common/enums"); const getWelcome = (req, res) => { res.render("welcome", { @@ -11,7 +12,54 @@ const getWelcome = (req, res) => { const postWelcome = async (req, res) => { const adType = parseInt(req.body.adType); + const publishAdType = parseInt(req.body.publishAdType); + let nextStepUrl = ""; + + if (adType) { + const adTypeStringId = getAdTypeString(adType); + try { + const newSearchRequest = await createSearchRequest({ + adType: adTypeStringId, + realEstateType: AD_CATEGORY.FLAT.id + }); + + nextStepUrl = `/vrstanekretnine/${newSearchRequest.id}`; + } catch (error) { + console.log(error); + nextStepUrl = `/`; + } + } else if (publishAdType) { + const adTypeStringId = getAdTypeString(publishAdType); + + try { + //First we create new Kivi Ad Original object in db then new Real Estate + //Problem with id-s + const newKiviOriginal = await createKiviOriginal({ + email: "" + }); + + const newRealEstate = await createRealEstate({ + adType: adTypeStringId, + realEstateType: AD_CATEGORY.FLAT.id, + //Temp variable because of the not null constraints + url: "http://localhost:5000/", + originAgencyName: AD_AGENCY.KIVI, + agencyObjectId: newKiviOriginal.kiviAdId + }); + + nextStepUrl = `/objavinekretninu/${newKiviOriginal.kiviAdId}`; + } catch (error) { + console.log(error); + nextStepUrl = `/`; + } + } + + res.redirect(nextStepUrl); +}; + +//--- Helper function +const getAdTypeString = adType => { const adTypeStringIds = { [AD_TYPE.AD_TYPE_SALE.id]: AD_TYPE.AD_TYPE_SALE.stringId, [AD_TYPE.AD_TYPE_RENT.id]: AD_TYPE.AD_TYPE_RENT.stringId @@ -20,20 +68,7 @@ const postWelcome = async (req, res) => { const adTypeStringId = adTypeStringIds[adType] || AD_TYPE.AD_TYPE_SALE.stringId; - let nextStepUrl = ""; - try { - const newSearchRequest = await createSearchRequest({ - adType: adTypeStringId, - realEstateType: AD_CATEGORY.FLAT.id - }); - - nextStepUrl = `/vrstanekretnine/${newSearchRequest.id}`; - } catch (error) { - console.log(error); - nextStepUrl = `/`; - } - - res.redirect(nextStepUrl); + return adTypeStringId; }; module.exports = { diff --git a/app/helpers/db/kiviOriginal.js b/app/helpers/db/kiviOriginal.js new file mode 100644 index 0000000..7e4d0f1 --- /dev/null +++ b/app/helpers/db/kiviOriginal.js @@ -0,0 +1,20 @@ +"use strict"; +const db = require("../../models/index"); +const sequelize = require("sequelize"); + +const createKiviOriginal = async (kiviAdFields = {}) => { + return await db.KiviOriginal.create(kiviAdFields); +}; + +const getKiviOriginalById = async id => { + try { + return db.KiviOriginal.findByPk(id); + } catch (error) { + console.log("kiviOriginal.js", error); + return null; + } +}; +module.exports = { + createKiviOriginal, + getKiviOriginalById +}; diff --git a/app/helpers/db/kiviOriginalAdsPhotos.js b/app/helpers/db/kiviOriginalAdsPhotos.js new file mode 100644 index 0000000..5533897 --- /dev/null +++ b/app/helpers/db/kiviOriginalAdsPhotos.js @@ -0,0 +1,17 @@ +"use strict"; +const db = require("../../models/index"); +const sequelize = require("sequelize"); + +const bulkUpsertKiviPhotos = async kiviPhotosData => { + try { + return await db.KiviOriginalAdsPhotos.bulkCreate(kiviPhotosData, { + ignoreDuplicates: true + }); + } catch (e) { + console.log("Error bulk upserting kiviOriginalAdsPhotos : ", e); + } +}; + +module.exports = { + bulkUpsertKiviPhotos +}; diff --git a/app/helpers/db/realEstate.js b/app/helpers/db/realEstate.js index 0f77260..17392ef 100644 --- a/app/helpers/db/realEstate.js +++ b/app/helpers/db/realEstate.js @@ -77,9 +77,28 @@ const bulkUpsertRealEstates = async realEstateData => { }; const getRealEstateById = async id => { - return db.RealEstate.findByPk(id); + try { + return db.RealEstate.findByPk(id); + } catch (error) { + console.log("realEstate.js", error); + return null; + } }; +const createRealEstate = async (realEstateFields = {}) => { + return await db.RealEstate.create(realEstateFields); +}; + +const findRealEstateByAgencyId = async kiviId => { + try { + return db.RealEstate.findOne({ + where: { agencyObjectId: kiviId } + }); + } catch (error) { + console.log("realEstate.js", error); + return null; + } +}; const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => { const { priceMin, @@ -344,5 +363,7 @@ const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => { module.exports = { bulkUpsertRealEstates, getRealEstateById, - findRealEstatesForSearchRequest + createRealEstate, + findRealEstatesForSearchRequest, + findRealEstateByAgencyId }; diff --git a/app/helpers/url.js b/app/helpers/url.js index 9ed771f..fcc1825 100644 --- a/app/helpers/url.js +++ b/app/helpers/url.js @@ -1,4 +1,6 @@ const { getSearchRequest } = require("./db/searchRequest"); +const { getRealEstateById } = require("./db/realEstate"); +const { getKiviOriginalById } = require("./db/kiviOriginal"); const currentSearchRequest = async req => { const searchRequestId = @@ -7,6 +9,23 @@ const currentSearchRequest = async req => { return await getSearchRequest(searchRequestId); }; -module.exports = { - currentSearchRequest + +const currentRealEstate = async req => { + const realEstateId = req && req.params ? req.params["realEstateId"] : null; + if (!realEstateId) return null; + + return await getRealEstateById(parseInt(realEstateId)); +}; +const currentKiviRealEstate = async req => { + const kiviRealEstateId = + req && req.params ? req.params["kiviRealEstateId"] : null; + if (!kiviRealEstateId) return null; + + return await getKiviOriginalById(kiviRealEstateId); +}; + +module.exports = { + currentSearchRequest, + currentRealEstate, + currentKiviRealEstate }; diff --git a/app/migrations/20200203114630-add-kiviOriginal-table.js b/app/migrations/20200203114630-add-kiviOriginal-table.js new file mode 100644 index 0000000..1fc294b --- /dev/null +++ b/app/migrations/20200203114630-add-kiviOriginal-table.js @@ -0,0 +1,28 @@ +"use strict"; + +module.exports = { + up: (queryInterface, Sequelize) => { + const tableFields = { + kiviAdId: { + type: Sequelize.UUID, + defaultValue: Sequelize.UUIDV4, + allowNull: false, + primaryKey: true + }, + email: Sequelize.TEXT, + createdAt: { + type: Sequelize.DATE, + defaultValue: Sequelize.literal("NOW()") + }, + updatedAt: { + type: Sequelize.DATE, + defaultValue: Sequelize.literal("NOW()") + } + }; + return queryInterface.createTable("KiviOriginal", tableFields); + }, + + down: (queryInterface, Sequelize) => { + return queryInterface.dropTable("KiviOriginal", {}); + } +}; diff --git a/app/migrations/20200312210336-add-table-kivi-photos.js b/app/migrations/20200312210336-add-table-kivi-photos.js new file mode 100644 index 0000000..cef01f5 --- /dev/null +++ b/app/migrations/20200312210336-add-table-kivi-photos.js @@ -0,0 +1,39 @@ +"use strict"; + +module.exports = { + up: (queryInterface, Sequelize) => { + const tableFields = { + id: { + type: Sequelize.BIGINT, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + kiviAdId: { + type: Sequelize.UUID, + allowNull: false, + references: { + model: "KiviOriginal", + key: "kiviAdId" + } + }, + photoUrl: { + type: Sequelize.TEXT, + allowNull: false + }, + createdAt: { + type: Sequelize.DATE, + defaultValue: Sequelize.literal("NOW()") + }, + updatedAt: { + type: Sequelize.DATE, + defaultValue: Sequelize.literal("NOW()") + } + }; + return queryInterface.createTable("KiviOriginalAdsPhotos", tableFields); + }, + + down: (queryInterface, Sequelize) => { + return queryInterface.dropTable("KiviOriginalAdsPhotos", {}); + } +}; diff --git a/app/models/kiviOriginal.js b/app/models/kiviOriginal.js new file mode 100644 index 0000000..75cd136 --- /dev/null +++ b/app/models/kiviOriginal.js @@ -0,0 +1,21 @@ +"use strict"; + +module.exports = (sequalize, DataTypes) => { + const KiviOriginal = sequalize.define( + "KiviOriginal", + { + kiviAdId: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + allowNull: false, + primaryKey: true + }, + email: DataTypes.TEXT + }, + { + freezeTableName: true + } + ); + + return KiviOriginal; +}; diff --git a/app/models/kiviOriginalAdsPhotos.js b/app/models/kiviOriginalAdsPhotos.js new file mode 100644 index 0000000..8321a0e --- /dev/null +++ b/app/models/kiviOriginalAdsPhotos.js @@ -0,0 +1,41 @@ +"use strict"; + +module.exports = (sequalize, DataTypes) => { + const KiviOriginalAdsPhotos = sequalize.define( + "KiviOriginalAdsPhotos", + { + id: { + type: DataTypes.BIGINT, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + kiviAdId: { + type: DataTypes.UUID, + allowNull: false, + references: { + model: "KiviOriginal", + key: "kiviAdId" + } + }, + photoUrl: { + type: DataTypes.TEXT, + allowNull: false + } + }, + { + freezeTableName: true + } + ); + + KiviOriginalAdsPhotos.associate = models => { + KiviOriginalAdsPhotos.hasMany(models.KiviOriginal, { + foreignKey: "kiviAdId", + sourceKey: "kiviAdId", + targetKey: "kiviAdId", + as: "kiviOriginal" + }); + }; + + return KiviOriginalAdsPhotos; +}; diff --git a/app/public/main.css b/app/public/main.css index 2258be6..21b588d 100644 --- a/app/public/main.css +++ b/app/public/main.css @@ -158,3 +158,27 @@ h3 { .estates-link { color: rgba(0, 0, 0, 0.87); } + +.error { + color: #cc0033; +} + +.custom-col { + margin-left: auto; + left: auto; + right: auto; +} + +.dropzone { + background: white; + border-radius: 10px; + border: 4px dashed #02adba; + border-image: none; + max-width: 80%; + margin-left: auto; + margin-right: auto; +} + +.dz-progress { + display: none; +} diff --git a/app/routes/index.js b/app/routes/index.js index 9043bbf..68a40a3 100644 --- a/app/routes/index.js +++ b/app/routes/index.js @@ -7,11 +7,20 @@ const { getRealEstateTypes, postRealEstateTypes } = require("../controllers/realEstateTypes"); +const { + getPublishTypes, + postPublishTypes +} = require("../controllers/publishRealEstateTypes"); +const { + getPublishInputs, + postPublishInputs +} = require("../controllers/publishRealEstate"); const { getQueryReview, postQueryReview } = require("../controllers/queryReview"); const { getGoAgain } = require("../controllers/goAgain"); +const { publishSuccess } = require("../controllers/publishSuccess"); const { getLocation, postLocation } = require("../controllers/location"); const { getUnsubscribe } = require("../controllers/unsubscribe"); const { getRealEstates } = require("../controllers/realEstates"); @@ -28,6 +37,14 @@ router.get("/vrstanekretnine", getRealEstateTypes); router.post("/vrstanekretnine/:searchRequestId", postRealEstateTypes); router.post("/vrstanekretnine", postRealEstateTypes); +router.get("/objavinekretninu/:kiviRealEstateId", getPublishTypes); +router.get("/objavinekretninu", getPublishTypes); +router.post("/objavinekretninu/:kiviRealEstateId", postPublishTypes); +router.post("/objavinekretninu", postPublishTypes); + +router.get("/podacionekretnini/:kiviRealEstateId", getPublishInputs); +router.post("/podacionekretnini/:kiviRealEstateId", postPublishInputs); + router.get("/lokacija/:searchRequestId", getLocation); router.post("/lokacija/:searchRequestId", postLocation); @@ -41,6 +58,8 @@ router.get("/odjava/:searchRequestId", getUnsubscribe); router.get("/ponovo", getGoAgain); +router.get("/uspjesnaobjava", publishSuccess); + router.get("/nekretnine/:searchRequestId", getRealEstates); router.get("/redirect/:id", getRedirect); diff --git a/app/views/layout.ejs b/app/views/layout.ejs index 7856ad6..c3349d1 100644 --- a/app/views/layout.ejs +++ b/app/views/layout.ejs @@ -9,13 +9,21 @@ gtag('js', new Date()); gtag('config', '<%= process.env.GA_ID %>'); - + + + + + + + @@ -47,6 +55,9 @@ <% } else { %> Kivi.ba <% } %> + + + diff --git a/app/views/publishAdditionalData.ejs b/app/views/publishAdditionalData.ejs new file mode 100644 index 0000000..17b8e95 --- /dev/null +++ b/app/views/publishAdditionalData.ejs @@ -0,0 +1,50 @@ +
+
+<% for (const input of additionalInputInputs){ %> +
+ + +
+<% } %> +
+ +
+
+ +<% for (const input of additionalBooleanPublishInputs){ %> +

+ +

+<% } %> +
+
+<% for (const input of additionalSegmentSelectInputs){ %> +
+

+ + <% for (const segmentObject of input.values) { %> + <% if (segmentObject.id!=="ANY") { %> + + <% } %> + <% } %> + +
+<% } %> \ No newline at end of file diff --git a/app/views/publishBasicData.ejs b/app/views/publishBasicData.ejs new file mode 100644 index 0000000..1aae179 --- /dev/null +++ b/app/views/publishBasicData.ejs @@ -0,0 +1,49 @@ +
+
+<% for (const input of basicInputInputs){ %> +
+ " + > + +
+<% } %> +
+
+
+<% for (const input of basicBooleanPublishInputs){ %> +

+ +

+<% } %> +
+ +
+<% for (const input of basicSegmentSelectInputs){ %> +
+

+ + <% for (const segmentObject of input.values) { %> + + <% } %> + +
+<% } %> + + + \ No newline at end of file diff --git a/app/views/publishEnd.ejs b/app/views/publishEnd.ejs new file mode 100644 index 0000000..452d005 --- /dev/null +++ b/app/views/publishEnd.ejs @@ -0,0 +1,24 @@ +
+
+

Vaš oglas je spreman!

+ Unesite kontakt email i objavite oglas. + +
+
+ +
+ +
+
+
+
+
+ Objavi oglas +
+
+ +
\ No newline at end of file diff --git a/app/views/publishLocation.ejs b/app/views/publishLocation.ejs new file mode 100644 index 0000000..d2dfd01 --- /dev/null +++ b/app/views/publishLocation.ejs @@ -0,0 +1,192 @@ +
+

+ Izaberite lokaciju nekretnine na mapi. +

+
+ +
+
+ +
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + diff --git a/app/views/publishPhotos.ejs b/app/views/publishPhotos.ejs new file mode 100644 index 0000000..3b28e75 --- /dev/null +++ b/app/views/publishPhotos.ejs @@ -0,0 +1,12 @@ +
+ +
+
+ +
+ +
+ +
+ + diff --git a/app/views/publishRealEstate.ejs b/app/views/publishRealEstate.ejs new file mode 100644 index 0000000..0e61eda --- /dev/null +++ b/app/views/publishRealEstate.ejs @@ -0,0 +1,290 @@ +
+
+
+ +
+ <%- include("./publishBasicData.ejs") %> +
+
+ <%- include("./publishAdditionalData.ejs") %> +
+
+ <%- include("./publishLocation.ejs") %> +
+
+ <%- include("./publishPhotos.ejs") %> +
+
+ <%- include("./publishEnd.ejs") %> +
+
+ + +
+ + \ No newline at end of file diff --git a/app/views/publishSuccess.ejs b/app/views/publishSuccess.ejs new file mode 100644 index 0000000..1696a01 --- /dev/null +++ b/app/views/publishSuccess.ejs @@ -0,0 +1,11 @@ +
+
+

Vaš oglas je spašen u Kivi bazu.

+
+
+ kivi logo +
+
+

Poslali smo potvrdni email sa detaljima oglasa na Vašu email adresu.

+ Nova pretraga +
\ No newline at end of file diff --git a/app/views/realEstateType.ejs b/app/views/realEstateType.ejs index 480478f..0610d27 100644 --- a/app/views/realEstateType.ejs +++ b/app/views/realEstateType.ejs @@ -9,7 +9,7 @@ <% if (selectedAdType === AD_TYPE.AD_TYPE_SALE.id) { %> checked <% } %>> - <%= AD_TYPE.AD_TYPE_SALE.title %> + <%= labelAdType[0] %> diff --git a/app/views/welcome.ejs b/app/views/welcome.ejs index 9ec6a94..845ec43 100644 --- a/app/views/welcome.ejs +++ b/app/views/welcome.ejs @@ -18,7 +18,20 @@ - +
+
Objavite svoj oglas.
+
+
+
+
+ Prodaj +
+
+ Iznajmi +
+
+ +
diff --git a/development.env b/development.env index bbfda0e..18bf56f 100644 --- a/development.env +++ b/development.env @@ -21,6 +21,9 @@ GA_ID=Google Analytics ID #=============== GOOGLE MAPS =============# API_MAP_KEY=(your-key-here) +#=============== GOOGLE STORAGE =============# +GOOGLE_APPLICATION_CREDENTIALS="Path to json key file" + #=============== AWS SDK EMAIL SETTINGS =======# AWS_KEY_ID=(your-key-here) AWS_SECRET_ACCESS_KEY=(your-key-here) diff --git a/help.js b/help.js new file mode 100644 index 0000000..cc0b55c --- /dev/null +++ b/help.js @@ -0,0 +1,237 @@ +(function() { + // Before using it we must add the parse and format functions + // Here is a sample implementation using moment.js + validate.extend(validate.validators.datetime, { + // The value is guaranteed not to be null or undefined but otherwise it + // could be anything. + parse: function(value, options) { + return +moment.utc(value); + }, + // Input is a unix timestamp + format: function(value, options) { + var format = options.dateOnly ? "YYYY-MM-DD" : "YYYY-MM-DD hh:mm:ss"; + return moment.utc(value).format(format); + } + }); + + // These are the constraints used to validate the form + var constraints = { + email: { + // Email is required + presence: true, + // and must be an email (duh) + email: true + }, + password: { + // Password is also required + presence: true, + // And must be at least 5 characters long + length: { + minimum: 5 + } + }, + "confirm-password": { + // You need to confirm your password + presence: true, + // and it needs to be equal to the other password + equality: { + attribute: "password", + message: "^The passwords does not match" + } + }, + username: { + // You need to pick a username too + presence: true, + // And it must be between 3 and 20 characters long + length: { + minimum: 3, + maximum: 20 + }, + format: { + // We don't allow anything that a-z and 0-9 + pattern: "[a-z0-9]+", + // but we don't care if the username is uppercase or lowercase + flags: "i", + message: "can only contain a-z and 0-9" + } + }, + birthdate: { + // The user needs to give a birthday + presence: true, + // and must be born at least 18 years ago + date: { + latest: moment().subtract(18, "years"), + message: "^You must be at least 18 years old to use this service" + } + }, + country: { + // You also need to input where you live + presence: true, + // And we restrict the countries supported to Sweden + inclusion: { + within: ["SE"], + // The ^ prevents the field name from being prepended to the error + message: "^Sorry, this service is for Sweden only" + } + }, + zip: { + // Zip is optional but if specified it must be a 5 digit long number + format: { + pattern: "\\d{5}" + } + }, + "number-of-children": { + presence: true, + // Number of children has to be an integer >= 0 + numericality: { + onlyInteger: true, + greaterThanOrEqualTo: 0 + } + } + }; + + // Hook up the form so we can prevent it from being posted + var form = document.querySelector("form#main"); + form.addEventListener("submit", function(ev) { + ev.preventDefault(); + handleFormSubmit(form); + }); + + // Hook up the inputs to validate on the fly + var inputs = document.querySelectorAll("input, textarea, select"); + for (var i = 0; i < inputs.length; ++i) { + inputs.item(i).addEventListener("change", function(ev) { + var errors = validate(form, constraints) || {}; + showErrorsForInput(this, errors[this.name]); + }); + } + + function handleFormSubmit(form, input) { + // validate the form against the constraints + var errors = validate(form, constraints); + // then we update the form to reflect the results + showErrors(form, errors || {}); + if (!errors) { + showSuccess(); + } + } + + // Updates the inputs with the validation errors + function showErrors(form, errors) { + // We loop through all the inputs and show the errors for that input + _.each(form.querySelectorAll("input[name], select[name]"), function(input) { + // Since the errors can be null if no errors were found we need to handle + // that + showErrorsForInput(input, errors && errors[input.name]); + }); + } + + // Shows the errors for a specific input + function showErrorsForInput(input, errors) { + // This is the root of the input + var formGroup = closestParent(input.parentNode, "form-group"), + // Find where the error messages will be insert into + messages = formGroup.querySelector(".messages"); + // First we remove any old messages and resets the classes + resetFormGroup(formGroup); + // If we have errors + if (errors) { + // we first mark the group has having errors + formGroup.classList.add("has-error"); + // then we append all the errors + _.each(errors, function(error) { + addError(messages, error); + }); + } else { + // otherwise we simply mark it as success + formGroup.classList.add("has-success"); + } + } + + // Recusively finds the closest parent that has the specified class + function closestParent(child, className) { + if (!child || child == document) { + return null; + } + if (child.classList.contains(className)) { + return child; + } else { + return closestParent(child.parentNode, className); + } + } + + function resetFormGroup(formGroup) { + // Remove the success and error classes + formGroup.classList.remove("has-error"); + formGroup.classList.remove("has-success"); + // and remove any old messages + _.each(formGroup.querySelectorAll(".help-block.error"), function(el) { + el.parentNode.removeChild(el); + }); + } + + // Adds the specified error with the following markup + //

[message]

+ function addError(messages, error) { + var block = document.createElement("p"); + block.classList.add("help-block"); + block.classList.add("error"); + block.innerText = error; + messages.appendChild(block); + } + + function showSuccess() { + // We made it \:D/ + alert("Success!"); + } +})(); +///////////////////////////////////////////////// +const isPresent = $input => { + return $input && $input!=="" && $input != null; +} + +const isNumber = $input => { + const simpleNumberRegex = /[+-]?(?:\d*[.,])?\d+/; + return $input && $input.length <250 && simpleNumberRegex.test($input); + +} + +const isInteger = $input => { + const simpleIntegerRegex = /^([+-]?[1-9]\d*|0)$/; + return $input && $input.length <250 && simpleIntegerRegex.test($input); + +} + +const validate = (input) => { + + const valid; + const errorMsg; + const constraint = input.constraint[0]; + + switch (constraint) { + case "required": + valid = isPresent ($(`#${input.dbField}`).val()); + errorMsg = ["Ovo je obavezno polje."]; + break; + case "numerical": + valid = isNumber ($(`#${input.dbField}`).val()); + errorMsg = ["Unesite brojcanu vrijednost."]; + break; + case "integer": + valid = isInteger ($(`#${input.dbField}`).val()); + errorMsg = ["Unesite cijeli broj."]; + + break; + default : + valid = true; + } + if (!valid) { + const inputField = document.querySelector(`#${input.dbField}`); + showErrorsForInput( inputField, errorMsg); + return false; + } else { + return true; + } + + } + diff --git a/help2.js b/help2.js new file mode 100644 index 0000000..622999b --- /dev/null +++ b/help2.js @@ -0,0 +1,112 @@ +const validatorFunction = () => { + // These are the constraints used to validate the form --just email for now! + const constraints = { + email: { + email: { + message: "Proba" + }, + // Email is required + presence: true, + // and must be an email (duh) + email: true + } + }; + + // Hook up the inputs to validate on the fly + const inputs = document.querySelectorAll("#email"); + // inputs.on("change", ev => { + // const errors = validate(form, constraints) || {}; + // showErrorsForInput(this, errors[this.name]); + // }); + // var inputs = document.querySelectorAll("input, textarea, select"); + for (var i = 0; i < inputs.length; ++i) { + inputs.item(i).addEventListener("change", function(ev) { + var errors = validate(form, constraints) || {}; + showErrorsForInput(this, errors[this.name]); + }); + } + + const handleFormSubmit = (form, input) => { + // validate the form against the constraints + const errors = validate(form, constraints); + // + console.log("handleFormSubmit error:", errors); + // then we update the form to reflect the results + showErrors(form, errors || {}); + if (!errors) { + showSuccess(); + } + }; + + // Updates the inputs with the validation errors + const showErrors = (form, errors) => { + // We loop through all the inputs and show the errors for that input + $.each(form.querySelectorAll("input[name], select[name]"), input => { + // Since the errors can be null if no errors were found we need to handle + // that + showErrorsForInput(input, errors && errors[input.name]); + }); + //showErrorsForInput(email, errors && errors[email]); + }; + + // Shows the errors for a specific input + const showErrorsForInput = (input, errors) => { + // This is the root of the input + const formGroup = closestParent(input.parentNode, "form-group"), + // Find where the error messages will be insert into + messages = formGroup.querySelector(".messages"); + // First we remove any old messages and resets the classes + resetFormGroup(formGroup); + // If we have errors + if (errors) { + // + console.log("errors:", errors); + // we first mark the group has having errors + formGroup.classList.add("has-error"); + // then we append all the errors + $.each(errors, error => { + addError(messages, errors[error]); + }); + } else { + // otherwise we simply mark it as success + formGroup.classList.add("has-success"); + } + }; + + // Recusively finds the closest parent that has the specified class + const closestParent = (child, className) => { + if (!child || child == document) { + return null; + } + if (child.classList.contains(className)) { + return child; + } else { + return closestParent(child.parentNode, className); + } + }; + + const resetFormGroup = formGroup => { + // Remove the success and error classes + formGroup.classList.remove("has-error"); + formGroup.classList.remove("has-success"); + // and remove any old messages + $.each(formGroup.querySelectorAll(".help-block.error"), el => { + el.parentNode.removeChild(el); + }); + }; + + // Adds the specified error with the following markup + //

[message]

+ const addError = (messages, error) => { + const block = document.createElement("p"); + block.classList.add("help-block"); + block.classList.add("error"); + block.innerText = error; + messages.appendChild(block); + }; + + const showSuccess = () => { + // We made it \:D/ + alert("Success!"); + }; +}; diff --git a/index.js b/index.js index 0669e7b..f8e62df 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,10 @@ const layout = require("express-layout"); const compression = require("compression"); const forceSSL = require("./app/helpers/forceSSL"); +const { Storage } = require("@google-cloud/storage"); +const validate = require("validate.js"); +const cors = require("cors"); + const { APP_PORT, CRAWLER_INTERVAL, @@ -19,6 +23,8 @@ const { const app = express(); +app.use(cors()); + app.use(forceSSL()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); @@ -50,3 +56,36 @@ const crawl = () => { setInterval(crawl, CRAWLER_INTERVAL * 1000); setInterval(checkUpNotify, 1000 * 60 * 60 * 24); + +//Google storage req +const PROJECT_ID = "marketalarm"; +const KEY_FILENAME = ""; //relative path +const BUCKET_NAME = "marketalarm-photos"; +const storage = new Storage(); + +const bucket = storage.bucket(BUCKET_NAME); + +app.get("/generateSignedURL", (req, res) => { + async function generateSignedUrl() { + // console.log("Started server function!"); + + const options = { + //Tried to define Google ID and private key while debugging + version: "v2", //tried v4 also + action: "write", + contentType: "image/*", //tried without and with specific image/png ex. + expires: Date.now() + 86400000 + }; + const filename = req.query.filename; + + // console.log("Filename: ", filename); + // console.log("Bucket name:", bucket.name); + + const [url] = await bucket.file(filename).getSignedUrl(options); + + //console.log(`The signed url is ${url}.`); + + res.status(200).send(url); + } + generateSignedUrl().catch(console.error); +}); diff --git a/marketalarm-cors.json b/marketalarm-cors.json new file mode 100644 index 0000000..784c4e9 --- /dev/null +++ b/marketalarm-cors.json @@ -0,0 +1,12 @@ +[ + { + "origin": ["*"], + "responseHeader": [ + "Content-Type", + "Access-Control-Allow-Origin", + "x-goog-resumable" + ], + "method": ["GET", "HEAD", "DELETE", "POST", "PUT", "OPTIONS"], + "maxAgeSeconds": 3600 + } +] diff --git a/marketalarm-d4b71394407e.json b/marketalarm-d4b71394407e.json new file mode 100644 index 0000000..672a051 --- /dev/null +++ b/marketalarm-d4b71394407e.json @@ -0,0 +1,12 @@ +{ + "type": "service_account", + "project_id": "marketalarm", + "private_key_id": "d4b71394407eb3dba9e431851dab60b198d6985d", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDTE6dkFr0bzDXg\n7ghMxkzq8cajqqqes9JZVqsXh+b/kFJYmEImFUILJJZdI080KM2sEYsIapBCxhMP\nFH017f/gfH3jnRbp3c70hghNh8noSTsq7kPA4l25o8GQnJ6AS+nhy8umPjb4KzX9\nkmC6OOD4P8mAmGqhoUv4s2jld1cXNur6NJjCpjEd2cH3SUbI71oA3V/4W8aK4dvS\n660kLY0PRt7mCiITe0hbTUBZY48W2ijZ7wM2r0HUtPG9XEeGMGmNsC+qD2oWxUU3\nvnm7l1fEIUvLYF4GrLRDJDSkpChBXNcWhoGV2AOvuTc+yghU2+lJWqrKcpLlI23E\nlVJjt9UhAgMBAAECggEABatr8sxq+SQOf9hSIe3Me9Kc1nunrC42scFHRKBNxahJ\ndXw5B9FQPh738Cqhk0xEz6hlrln1Agj6HhRIz8U0r9R+z4TRRr6kfnWmBZAMShu0\nC4JW448abpAYx8CQ/CvRmq2GlF+/M+QBeqpLS8gPzyaKTB/5IBaKG8Bn0fXXQZ2e\n7RaTpGx62jq79omPwiKz0PMVBGZrzPu8Z4tW47muV51osdKSNVgsXb4gCZl28zN2\n6zzY1ZK7u89MesY8joILMHm8cw0oyv9o+RVGEa1I1nq2q1A8ftZny4p7kUA/ITZX\nEZ6SCOP87z9HeVCr8lzexcovD8uZCOTYpcfotlSjGQKBgQD1VVGU2bzExiV0XQGc\n8n6m4TR6Y9zwXBiQPe0rXPZhvsj8QMTXk+L0ejCo4m7NF1dEyH6u+qX6wjNL1Hm9\nN/ZuDFqYtd9w6cQ8CtDZ6QZIE60k6tLQhMNRNMvHdMfedq+VOz3LX6TdyTnv8dP3\nbEsD8wIfFd6t5wNgeZkbKsNxBwKBgQDcQQsUppjglGpUoz7lGHKbFcKMPpIj9fMY\nfze1DXeTAtHGxGm2F10WZvxOEs4DCOUllBlarL5xDAJIJHk/NYlgnI6MJXMLro6d\nsb4iNTUuJKdqAijyOaZQUADJpdYKGwu5y66PUOuojWFV9kiamquXduJ9jzOa1vr1\nSJPUy2YGlwKBgQDNJrpgwa8z0QozAy89Ih68x+fNTMLNkAXOYKp6L3OsixCguDyi\nlP0dOSyFnUvQXutQDmS5R8oSJeElURk4HJsKrXP47WVak3DQUK8S+eSR0zpfe6os\nSkjWGFMriEE2i4MKRI7JCULhX8r+FfgNl9YnCEfG3M/oFhzhyO06JYlncwKBgF6n\nBSAGyEQbA+cDkI/bhcToAQdMDHmvxJyOb147P1vKJmSJG/TI7ZQnBd53blkXhYI0\ntwCko+LpCkH+iqyDUVpXbVsE7P/kMB3MuKzyuLvvvJJuAzK1W6e/+daukeEd5lge\nFBI68EsrFt1eTa1DMuKQkJzs4Xx1TrwCSKV2E45ZAoGBAIZkXyAOhwqCxwDF7B69\nt/7CWs0gPGqp6lFO7fgt7jPmcmSEr/xgUbBDFwd7D49jpXVgCEtr1Bd6MItlu/Ns\nXgXyOa5LPQmglF7UtnvuQLASBy5X6boKaf3sz7I5eho1kXczPGQUHfR5e0DaTND3\nTi2NLIAUci8T7hc8mONdeEHD\n-----END PRIVATE KEY-----\n", + "client_email": "marketalarm-photos-service-acc@marketalarm.iam.gserviceaccount.com", + "client_id": "115644068453290488813", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/marketalarm-photos-service-acc%40marketalarm.iam.gserviceaccount.com" +} diff --git a/package-lock.json b/package-lock.json index 4626180..ba4349f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,92 @@ "request": "2.x.x" } }, + "@google-cloud/common": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-2.4.0.tgz", + "integrity": "sha512-zWFjBS35eI9leAHhjfeOYlK5Plcuj/77EzstnrJIZbKgF/nkqjcQuGiMCpzCwOfPyUbz8ZaEOYgbHa759AKbjg==", + "requires": { + "@google-cloud/projectify": "^1.0.0", + "@google-cloud/promisify": "^1.0.0", + "arrify": "^2.0.0", + "duplexify": "^3.6.0", + "ent": "^2.2.0", + "extend": "^3.0.2", + "google-auth-library": "^5.5.0", + "retry-request": "^4.0.0", + "teeny-request": "^6.0.0" + } + }, + "@google-cloud/paginator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-2.0.3.tgz", + "integrity": "sha512-kp/pkb2p/p0d8/SKUu4mOq8+HGwF8NPzHWkj+VKrIPQPyMRw8deZtrO/OcSiy9C/7bpfU5Txah5ltUNfPkgEXg==", + "requires": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + } + }, + "@google-cloud/projectify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.4.tgz", + "integrity": "sha512-ZdzQUN02eRsmTKfBj9FDL0KNDIFNjBn/d6tHQmA/+FImH5DO6ZV8E7FzxMgAUiVAUq41RFAkb25p1oHOZ8psfg==" + }, + "@google-cloud/promisify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.4.tgz", + "integrity": "sha512-VccZDcOql77obTnFh0TbNED/6ZbbmHDf8UMNnzO1d5g9V0Htfm4k5cllY8P1tJsRKC3zWYGRLaViiupcgVjBoQ==" + }, + "@google-cloud/storage": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-4.5.0.tgz", + "integrity": "sha512-ZLFcR6CiP1AnYBA9eTtASF9Dy3wjYmGx+HZiy/LsIPN41wyBTn9yAjIOxRHiteqzX3uQzZ+VJNCB/DmTU33CeQ==", + "requires": { + "@google-cloud/common": "^2.1.1", + "@google-cloud/paginator": "^2.0.0", + "@google-cloud/promisify": "^1.0.0", + "arrify": "^2.0.0", + "compressible": "^2.0.12", + "concat-stream": "^2.0.0", + "date-and-time": "^0.12.0", + "duplexify": "^3.5.0", + "extend": "^3.0.2", + "gaxios": "^2.0.1", + "gcs-resumable-upload": "^2.2.4", + "hash-stream-validation": "^0.2.2", + "mime": "^2.2.0", + "mime-types": "^2.0.8", + "onetime": "^5.1.0", + "p-limit": "^2.2.0", + "pumpify": "^2.0.0", + "readable-stream": "^3.4.0", + "snakeize": "^0.1.0", + "stream-events": "^1.0.1", + "through2": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" + } + } + }, "@sendgrid/client": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-6.3.0.tgz", @@ -40,6 +126,11 @@ "@sendgrid/helpers": "^6.3.0" } }, + "@tootallnate/once": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.0.0.tgz", + "integrity": "sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA==" + }, "@types/caseless": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", @@ -79,6 +170,14 @@ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, "accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", @@ -88,6 +187,29 @@ "negotiator": "0.6.1" } }, + "agent-base": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", + "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "ajv": { "version": "6.10.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", @@ -176,6 +298,11 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -320,6 +447,11 @@ "tweetnacl": "^0.14.3" } }, + "bignumber.js": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", + "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" + }, "binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", @@ -424,6 +556,16 @@ "isarray": "^1.0.0" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, "buffer-writer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", @@ -671,6 +813,17 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "config-chain": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", @@ -730,6 +883,15 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "create-error-class": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", @@ -799,6 +961,11 @@ "assert-plus": "^1.0.0" } }, + "date-and-time": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.12.0.tgz", + "integrity": "sha512-n2RJIAp93AucgF/U/Rz5WRS2Hjg5Z+QxscaaMCi6pVZT1JpJKRH+C08vyH/lRR1kxNXnPxgo3lWfd+jCb/UcuQ==" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -949,6 +1116,41 @@ "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", "dev": true }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -958,6 +1160,14 @@ "safer-buffer": "^2.1.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "editorconfig": { "version": "0.15.3", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", @@ -997,6 +1207,11 @@ "once": "^1.4.0" } }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=" + }, "entities": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", @@ -1074,6 +1289,11 @@ "es5-ext": "~0.10.14" } }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", @@ -1289,6 +1509,11 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, + "fast-text-encoding": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.1.tgz", + "integrity": "sha512-x4FEgaz3zNRtJfLFqJmHWxkMDDvXVtaznj2V9jiP8ACUJrUgist4bP9FmDL2Vew2Y9mEQI/tG4GqabaitYp9CQ==" + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -1947,6 +2172,117 @@ } } }, + "gaxios": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.2.tgz", + "integrity": "sha512-K/+py7UvKRDaEwEKlLiRKrFr+wjGjsMz5qH7Vs549QJS7cpSCOT/BbWL7pzqECflc46FcNPipjSfB+V1m8PAhw==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.3.0" + }, + "dependencies": { + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + } + } + }, + "gcp-metadata": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.5.0.tgz", + "integrity": "sha512-ZQf+DLZ5aKcRpLzYUyBS3yo3N0JSa82lNDO8rj3nMSlovLcz2riKFBsYgDzeXcv75oo5eqB2lx+B14UvPoCRnA==", + "requires": { + "gaxios": "^2.1.0", + "json-bigint": "^0.3.0" + } + }, + "gcs-resumable-upload": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-2.3.2.tgz", + "integrity": "sha512-OPS0iAmPCV+r7PziOIhyxmQOzsazFCy76yYDOS/Z80O/7cuny1KMfqDQa2T0jLaL8EreTU7EMZG5pUuqBKgzHA==", + "requires": { + "abort-controller": "^3.0.0", + "configstore": "^5.0.0", + "gaxios": "^2.0.0", + "google-auth-library": "^5.0.0", + "pumpify": "^2.0.0", + "stream-events": "^1.0.4" + }, + "dependencies": { + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + }, + "dot-prop": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "requires": { + "is-obj": "^2.0.0" + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "requires": { + "semver": "^6.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" + } + } + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2017,6 +2353,45 @@ "ini": "^1.3.4" } }, + "google-auth-library": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz", + "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^2.1.0", + "gcp-metadata": "^3.4.0", + "gtoken": "^4.1.0", + "jws": "^4.0.0", + "lru-cache": "^5.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "google-p12-pem": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.4.tgz", + "integrity": "sha512-S4blHBQWZRnEW44OcR7TL9WR+QCqByRvhNDZ/uuQfpxywfupikf/miba8js1jZi6ZOGv5slgSuoshCWh6EMDzg==", + "requires": { + "node-forge": "^0.9.0" + } + }, "got": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", @@ -2049,6 +2424,24 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" }, + "gtoken": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.4.tgz", + "integrity": "sha512-VxirzD0SWoFUo5p8RDP8Jt2AGyOmyYcT/pOUgDKJCK+iSw0TMqwrVfY37RXTNmoKwrzmDHSk0GMT9FsgVmnVSA==", + "requires": { + "gaxios": "^2.1.0", + "google-p12-pem": "^2.0.0", + "jws": "^4.0.0", + "mime": "^2.2.0" + }, + "dependencies": { + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + } + } + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -2100,6 +2493,47 @@ } } }, + "hash-stream-validation": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/hash-stream-validation/-/hash-stream-validation-0.2.2.tgz", + "integrity": "sha512-cMlva5CxWZOrlS/cY0C+9qAzesn5srhFA8IT1VPiHc9bWWBLkJfEUIZr7MWoi89oOOGmpg8ymchaOjiArsGu5A==", + "requires": { + "through2": "^2.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -2147,6 +2581,31 @@ "statuses": ">= 1.4.0 < 2" } }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -2157,6 +2616,30 @@ "sshpk": "^1.7.0" } }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "iconv-lite": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", @@ -2185,8 +2668,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "inflection": { "version": "1.12.0", @@ -2480,6 +2962,14 @@ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, + "json-bigint": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", + "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=", + "requires": { + "bignumber.js": "^7.0.0" + } + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -2514,6 +3004,25 @@ "verror": "1.10.0" } }, + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -2801,6 +3310,11 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz", "integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==" }, + "node-forge": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.1.tgz", + "integrity": "sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ==" + }, "node-schedule": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-1.3.2.tgz", @@ -2887,6 +3401,11 @@ "resolved": "https://registry.npmjs.org/obj-extend/-/obj-extend-0.1.0.tgz", "integrity": "sha1-u0SKR3X7les0p4H5CLusLfI9u1s=" }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -2962,6 +3481,14 @@ "wrappy": "1" } }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -3218,8 +3745,7 @@ "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "proto-list": { "version": "1.2.4", @@ -3260,6 +3786,29 @@ "once": "^1.3.1" } }, + "pumpify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", + "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", + "requires": { + "duplexify": "^4.1.1", + "inherits": "^2.0.3", + "pump": "^3.0.0" + }, + "dependencies": { + "duplexify": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", + "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + } + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -3492,6 +4041,30 @@ "any-promise": "^1.3.0" } }, + "retry-request": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.1.tgz", + "integrity": "sha512-BINDzVtLI2BDukjWmjAIRZ0oglnCAkpP2vQjM3jdLhmT62h0xnQgciPwBRDAvHqpkPT2Wo1XuUyLyn6nbGrZQQ==", + "requires": { + "debug": "^4.1.1", + "through2": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -3684,6 +4257,11 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, + "snakeize": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/snakeize/-/snakeize-0.1.0.tgz", + "integrity": "sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0=" + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -3880,6 +4458,19 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" }, + "stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "requires": { + "stubs": "^3.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -3926,6 +4517,11 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, + "stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=" + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -3934,6 +4530,18 @@ "has-flag": "^3.0.0" } }, + "teeny-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-6.0.2.tgz", + "integrity": "sha512-B6fxA0fSnY/bul06NggdN1nywtr5U5Uvt96pHfTi8pi4MNe6++VUWcAAFBrcMeha94s+gULwA5WvagoSZ+AcYg==", + "requires": { + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.2.0", + "stream-events": "^1.0.5", + "uuid": "^3.3.2" + } + }, "term-size": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", @@ -3982,6 +4590,14 @@ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "requires": { + "readable-stream": "2 || 3" + } + }, "timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -4107,6 +4723,19 @@ "mime-types": "~2.1.18" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, "umzug": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.2.0.tgz", @@ -4309,6 +4938,11 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, + "validate.js": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/validate.js/-/validate.js-0.13.1.tgz", + "integrity": "sha512-PnFM3xiZ+kYmLyTiMgTYmU7ZHkjBZz2/+F0DaALc/uUtVzdCt1wAosvYJ5hFQi/hz8O4zb52FQhHZRC+uVkJ+g==" + }, "validator": { "version": "10.11.0", "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", diff --git a/package.json b/package.json index 3bc99a7..69ff7f1 100644 --- a/package.json +++ b/package.json @@ -30,11 +30,13 @@ }, "dependencies": { "2checkout-node": "0.0.1", + "@google-cloud/storage": "^4.5.0", "@sendgrid/mail": "^6.3.1", "aws-sdk": "^2.422.0", "bluebird": "^3.5.5", "cheerio": "^1.0.0-rc.2", "compression": "^1.7.4", + "cors": "^2.8.5", "dotenv": "^7.0.0", "ejs": "^2.6.1", "eslint-plugin-prettier": "^3.1.2", @@ -51,7 +53,8 @@ "prettier": "^1.19.1", "react-step-wizard": "^5.1.0", "sequelize": "^5.18.4", - "sequelize-cli": "^5.5.0" + "sequelize-cli": "^5.5.0", + "validate.js": "^0.13.1" }, "devDependencies": { "nodemon": "^1.19.0"