diff --git a/config/deno-kv.ts b/config/deno-kv.ts index ab1bada..334a8df 100644 --- a/config/deno-kv.ts +++ b/config/deno-kv.ts @@ -1,5 +1,24 @@ +import type { Transaction } from "postgres"; +import { dbPool } from "./postgres.ts"; + let db: Deno.Kv | null = null; +export interface KvMessage { + type: "CREATE_LISTING" | "PROPERTY_VALUATION"; + source: "LAMUDI" | "APP"; + // deno-lint-ignore no-explicit-any + data: any; +} + +export interface Location { + listing_area_id?: string; + area?: string; + listing_city_id: string; + city: string; + listing_region_id: string; + region: string; +} + export async function getKvInstance(): Promise { if (!db) { db = await Deno.openKv(); @@ -20,9 +39,282 @@ export async function sendMessage(arg: { await kv.enqueue(data, options); } +async function getLocation(transaction: Transaction, dataLayer: Location) { + let region = await transaction.queryObject(` + SELECT id, listing_region_id + FROM Listing_Region + WHERE region = '${dataLayer.region}' + `); + + let city = await transaction.queryObject(` + SELECT id, listing_city_id + FROM Listing_City + WHERE city = '${dataLayer.city}' + `); + + let area = await transaction.queryObject(` + SELECT id + FROM Listing_Area + WHERE listing_area_id = '${dataLayer?.listing_area_id || null}' + `); + + if (region.rowCount === 0) { + region = await transaction.queryObject(` + INSERT INTO Listing_Region (region, listing_region_id) + VALUES ('${dataLayer.region}', '${dataLayer.listing_region_id}') + RETURNING id, listing_region_id + `); + } + + if (city.rowCount === 0) { + const createdRegion = region.rows[0] as { listing_region_id: number }; + + city = await transaction.queryObject(` + INSERT INTO Listing_City (city, listing_city_id, listing_region_id) + VALUES ('${dataLayer.city}', '${dataLayer.listing_city_id}', '${createdRegion.listing_region_id}') + RETURNING id, listing_city_id + `); + } + + if (area.rowCount === 0) { + area = await transaction.queryObject(` + INSERT INTO Listing_Area (area, listing_area_id) + VALUES ('${dataLayer.area}', '${dataLayer.listing_area_id}') + RETURNING id + `); + } + + return { + region: region.rows[0] as { id: number; listing_region_id: number }, + city: city.rows[0] as { id: number; listing_city_id: number }, + area: area.rows[0] as { id: number }, + }; +} + +function cleanSpecialCharacters(input: string): string { + if (!input) return "No description"; + + // Encode special characters to ensure they are properly interpreted by the SQL engine + const encodedString = encodeURIComponent(input); + + // Remove emojis and other special characters + const cleanedString = encodedString.replace( + /[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E0}-\u{1F1FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/gu, + "" + ); + + // Remove extra whitespace + const trimmedString = cleanedString.replace(/\s+/g, " ").trim(); + + // Remove any remaining non-printable characters + return trimmedString.replace(/[^\x20-\x7E]/g, ""); +} + export async function listenQueue(kv: Deno.Kv) { - await kv.listenQueue((msg) => { - console.log(msg); + await kv.listenQueue(async (msg: KvMessage) => { + switch (msg.type) { + case "CREATE_LISTING": + if (msg.source === "LAMUDI" && msg.data.listingUrl && msg.data) { + const handleCondominium = async () => { + let transaction; + const client_1 = await dbPool.connect(); + + try { + const listingUrl = msg.data.listingUrl; + const isCondominium = + msg.data?.dataLayer?.attributes?.attribute_set_name === + "Condominium"; + + transaction = client_1.createTransaction("create-listing"); + await transaction.begin(); + + const listingId = await transaction.queryObject(` + SELECT id + FROM Listing + WHERE url = '${listingUrl}' OR title = '${msg.data.dataLayer?.title}' + `); + + if (listingId?.rowCount && listingId.rowCount > 0) { + throw new Error("Listing already exists"); + } + + if (!msg.data?.dataLayer) { + throw new Error("DataLayer is missing or undefined"); + } + + if (!msg.data.dataLayer.agent_name) { + throw new Error("Agent name is missing or undefined"); + } + + if ( + !msg.data.dataLayer.location || + typeof msg.data.dataLayer.location !== "object" + ) { + throw new Error( + "Location is missing, undefined, or not an object" + ); + } + + if ( + !msg.data.dataLayer.attributes || + Object.keys(msg.data.dataLayer.attributes).length < 5 + ) { + throw new Error( + "Attributes are missing, undefined, or have fewer than 5 properties" + ); + } + + if (isCondominium) { + const propertyTypeId = 1; + const images = msg.data.images as { src: string }[]; + const agentName = msg.data.dataLayer.agent_name; + const location: Location = msg.data.dataLayer.location; + const dataLayerAttributes = msg.data.dataLayer.attributes; + const offerTypeId = + dataLayerAttributes.offer_type === "Rent" ? 2 : 1; + const locationData = await getLocation(transaction, { + ...location, + listing_area_id: dataLayerAttributes?.listing_area_id, + }); + + const { region, city, area } = locationData; + + const property = await transaction.queryArray({ + args: [ + dataLayerAttributes?.floor_size || 0, + dataLayerAttributes?.lot_size || 0, + dataLayerAttributes?.building_size || 0, + dataLayerAttributes?.ceiling_height || 0, + dataLayerAttributes?.bedrooms || 0, + dataLayerAttributes?.bathrooms || 0, + dataLayerAttributes?.car_spaces || 0, + dataLayerAttributes.location_longitude, + dataLayerAttributes.location_latitude, + dataLayerAttributes?.year_built || 0, + dataLayerAttributes?.image_url || null, + JSON.stringify(images.map((image) => image.src)), + JSON.stringify(dataLayerAttributes?.amenities || {}), + JSON.stringify( + dataLayerAttributes?.property_features || {} + ), + JSON.stringify(dataLayerAttributes?.indoor_features || {}), + JSON.stringify(dataLayerAttributes?.outdoor_features || {}), + propertyTypeId, + dataLayerAttributes?.address || null, + region.id, + city.id, + area.id, + ], + text: ` + INSERT INTO property ( + floor_size, + lot_size, + building_size, + ceiling_height, + no_of_bedrooms, + no_of_bathrooms, + no_of_parking_spaces, + longitude, + latitude, + year_built, + primary_image_url, + images, + amenities, + property_features, + indoor_features, + outdoor_features, + property_type_id, + address, + listing_region_id, + listing_city_id, + listing_area_id + ) VALUES ( + $1, + $2, + $3, + $4, + $5, + $6, + $7, + $8, + $9, + $10, + $11, + $12, + $13, + $14, + $15, + $16, + $17, + $18, + $19, + $20, + $21 + ) RETURNING id + `, + }); + + const newProperty = property.rows[0][0] as number; + + const address = `${ + dataLayerAttributes?.listing_area + ? `${dataLayerAttributes.listing_area}, ` + : "" + }${dataLayerAttributes.listing_city}`; + + await transaction.queryArray({ + args: [ + msg.data.dataLayer?.title, + `https://www.lamudi.com.ph/${dataLayerAttributes?.urlkey_details}`, + dataLayerAttributes?.project_name || null, + cleanSpecialCharacters( + msg.data.dataLayer?.description?.text + ), + true, + address, + dataLayerAttributes?.price_formatted + ? `${dataLayerAttributes?.price_formatted}` + : null, + dataLayerAttributes?.price || 0, + offerTypeId, + newProperty, + ], + text: `INSERT INTO Listing (title, url, project_name, description, is_scraped, address, price_formatted, price, offer_type_id, property_id) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id`, + }); + + await transaction.commit(); + console.log("Transaction successfully committed"); + } + } catch (error) { + if (transaction) { + console.log("Transaction rollback"); + await transaction.rollback(); + } + throw error; + } finally { + console.log("Connection released"); + client_1.release(); + } + }; + + try { + await handleCondominium(); + // deno-lint-ignore no-explicit-any + } catch (error: any) { + console.error( + "Failed to handle condominium:", + error?.message || error + ); + } + } + break; + case "PROPERTY_VALUATION": + if (msg.source === "APP") { + console.log(JSON.stringify(msg, null, 2)); + } + break; + } }); } diff --git a/config/postgres.ts b/config/postgres.ts index 40a5bac..cf89512 100644 --- a/config/postgres.ts +++ b/config/postgres.ts @@ -1,15 +1,8 @@ -import { Pool } from "jsr:@wok/pg-driver"; +import { Pool } from "postgres"; const POOL_CONNECTIONS = 20; -const config = { - database: Deno.env.get("DB_NAME"), - hostname: Deno.env.get("DB_HOST"), - password: Deno.env.get("DB_PASSWORD"), - port: parseInt(Deno.env.get("DB_PORT") || "", 10), - user: Deno.env.get("DB_USER"), -}; -export const dbPool = new Pool(config, POOL_CONNECTIONS); +export const dbPool = new Pool(Deno.env.get("DATABASE_URL"), POOL_CONNECTIONS); export async function runQuery(query: string) { using client = await dbPool.connect(); diff --git a/deno.json b/deno.json index 38a476c..8fb6230 100644 --- a/deno.json +++ b/deno.json @@ -1,5 +1,4 @@ { - "auto": true, "entryPoint": "server.ts", "nodeModulesDir": "auto", "imports": { @@ -7,7 +6,7 @@ "@hono/clerk-auth": "npm:@hono/clerk-auth@^2.0.0", "@hono/hono": "jsr:@hono/hono@^4.6.5", "@std/dotenv": "jsr:@std/dotenv@^0.225.2", - "@wok/pg-driver": "jsr:@wok/pg-driver@^0.19.4", + "postgres": "https://deno.land/x/postgres@v0.19.3/mod.ts", "zod": "npm:zod@^3.23.8" }, "tasks": { diff --git a/deno.lock b/deno.lock index b16d2ca..ecd17d6 100644 --- a/deno.lock +++ b/deno.lock @@ -1,19 +1,10 @@ { "version": "4", "specifiers": { + "jsr:@hono/hono@*": "4.6.5", "jsr:@hono/hono@^4.6.5": "4.6.5", - "jsr:@std/async@1": "1.0.6", - "jsr:@std/bytes@^1.0.2": "1.0.2", - "jsr:@std/crypto@^1.0.1": "1.0.3", - "jsr:@std/datetime@~0.224.3": "0.224.5", "jsr:@std/dotenv@*": "0.225.2", "jsr:@std/dotenv@~0.225.2": "0.225.2", - "jsr:@std/encoding@^1.0.1": "1.0.5", - "jsr:@std/fmt@~0.225.6": "0.225.6", - "jsr:@std/io@~0.224.3": "0.224.9", - "jsr:@std/path@^1.0.1": "1.0.6", - "jsr:@wok/pg-driver@*": "0.19.4", - "jsr:@wok/pg-driver@~0.19.4": "0.19.4", "npm:@clerk/backend@^1.13.9": "1.13.9", "npm:@hono/clerk-auth@2": "2.0.0_@clerk+backend@1.13.9_hono@4.6.4", "npm:zod@^3.23.8": "3.23.8" @@ -22,48 +13,8 @@ "@hono/hono@4.6.5": { "integrity": "68efe4a0ab7c4fb082cb71aa894a25e1c6cfad6d124dc943471e6758a7a1bdee" }, - "@std/async@1.0.6": { - "integrity": "6d262156dd35c4a72ee1a2f8679be40264f370cfb92e2e13d4eca2ae05e16f34" - }, - "@std/bytes@1.0.2": { - "integrity": "fbdee322bbd8c599a6af186a1603b3355e59a5fb1baa139f8f4c3c9a1b3e3d57" - }, - "@std/crypto@1.0.3": { - "integrity": "a2a32f51ddef632d299e3879cd027c630dcd4d1d9a5285d6e6788072f4e51e7f" - }, - "@std/datetime@0.224.5": { - "integrity": "363fca6e2e46c1e85139c10ba77745d25c0936abd112b8bfdc9b8fc3615added" - }, "@std/dotenv@0.225.2": { "integrity": "e2025dce4de6c7bca21dece8baddd4262b09d5187217e231b033e088e0c4dd23" - }, - "@std/encoding@1.0.5": { - "integrity": "ecf363d4fc25bd85bd915ff6733a7e79b67e0e7806334af15f4645c569fefc04" - }, - "@std/fmt@0.225.6": { - "integrity": "aba6aea27f66813cecfd9484e074a9e9845782ab0685c030e453a8a70b37afc8" - }, - "@std/io@0.224.9": { - "integrity": "4414664b6926f665102e73c969cfda06d2c4c59bd5d0c603fd4f1b1c840d6ee3", - "dependencies": [ - "jsr:@std/bytes" - ] - }, - "@std/path@1.0.6": { - "integrity": "ab2c55f902b380cf28e0eec501b4906e4c1960d13f00e11cfbcd21de15f18fed" - }, - "@wok/pg-driver@0.19.4": { - "integrity": "78942ba8c301900a76f43246f8f33bcfa80c2ed521fde750751f51ad8e140ff2", - "dependencies": [ - "jsr:@std/async", - "jsr:@std/bytes", - "jsr:@std/crypto", - "jsr:@std/datetime", - "jsr:@std/encoding", - "jsr:@std/fmt", - "jsr:@std/io", - "jsr:@std/path" - ] } }, "npm": { @@ -198,11 +149,135 @@ "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==" } }, + "remote": { + "https://deno.land/std@0.214.0/assert/assert.ts": "bec068b2fccdd434c138a555b19a2c2393b71dfaada02b7d568a01541e67cdc5", + "https://deno.land/std@0.214.0/assert/assertion_error.ts": "9f689a101ee586c4ce92f52fa7ddd362e86434ffdf1f848e45987dc7689976b8", + "https://deno.land/std@0.214.0/async/delay.ts": "8e1d18fe8b28ff95885e2bc54eccec1713f57f756053576d8228e6ca110793ad", + "https://deno.land/std@0.214.0/bytes/copy.ts": "f29c03168853720dfe82eaa57793d0b9e3543ebfe5306684182f0f1e3bfd422a", + "https://deno.land/std@0.214.0/crypto/_fnv/fnv32.ts": "ba2c5ef976b9f047d7ce2d33dfe18671afc75154bcf20ef89d932b2fe8820535", + "https://deno.land/std@0.214.0/crypto/_fnv/fnv64.ts": "580cadfe2ff333fe253d15df450f927c8ac7e408b704547be26aab41b5772558", + "https://deno.land/std@0.214.0/crypto/_fnv/mod.ts": "8dbb60f062a6e77b82f7a62ac11fabfba52c3cd408c21916b130d8f57a880f96", + "https://deno.land/std@0.214.0/crypto/_fnv/util.ts": "27b36ce3440d0a180af6bf1cfc2c326f68823288540a354dc1d636b781b9b75f", + "https://deno.land/std@0.214.0/crypto/_wasm/lib/deno_std_wasm_crypto.generated.mjs": "76c727912539737def4549bb62a96897f37eb334b979f49c57b8af7a1617635e", + "https://deno.land/std@0.214.0/crypto/_wasm/mod.ts": "c55f91473846827f077dfd7e5fc6e2726dee5003b6a5747610707cdc638a22ba", + "https://deno.land/std@0.214.0/crypto/crypto.ts": "4448f8461c797adba8d70a2c60f7795a546d7a0926e96366391bffdd06491c16", + "https://deno.land/std@0.214.0/datetime/_common.ts": "a62214c1924766e008e27d3d843ceba4b545dc2aa9880de0ecdef9966d5736b6", + "https://deno.land/std@0.214.0/datetime/parse.ts": "bb248bbcb3cd54bcaf504a1ee670fc4695e429d9019c06af954bbe2bcb8f1d02", + "https://deno.land/std@0.214.0/encoding/_util.ts": "beacef316c1255da9bc8e95afb1fa56ed69baef919c88dc06ae6cb7a6103d376", + "https://deno.land/std@0.214.0/encoding/base64.ts": "96e61a556d933201266fea84ae500453293f2aff130057b579baafda096a96bc", + "https://deno.land/std@0.214.0/encoding/hex.ts": "4d47d3b25103cf81a2ed38f54b394d39a77b63338e1eaa04b70c614cb45ec2e6", + "https://deno.land/std@0.214.0/fmt/colors.ts": "aeaee795471b56fc62a3cb2e174ed33e91551b535f44677f6320336aabb54fbb", + "https://deno.land/std@0.214.0/io/buf_reader.ts": "c73aad99491ee6db3d6b001fa4a780e9245c67b9296f5bad9c0fa7384e35d47a", + "https://deno.land/std@0.214.0/io/buf_writer.ts": "f82f640c8b3a820f600a8da429ad0537037c7d6a78426bbca2396fb1f75d3ef4", + "https://deno.land/std@0.214.0/io/types.ts": "748bbb3ac96abda03594ef5a0db15ce5450dcc6c0d841c8906f8b10ac8d32c96", + "https://deno.land/std@0.214.0/path/_common/assert_path.ts": "2ca275f36ac1788b2acb60fb2b79cb06027198bc2ba6fb7e163efaedde98c297", + "https://deno.land/std@0.214.0/path/_common/basename.ts": "569744855bc8445f3a56087fd2aed56bdad39da971a8d92b138c9913aecc5fa2", + "https://deno.land/std@0.214.0/path/_common/common.ts": "6157c7ec1f4db2b4a9a187efd6ce76dcaf1e61cfd49f87e40d4ea102818df031", + "https://deno.land/std@0.214.0/path/_common/constants.ts": "dc5f8057159f4b48cd304eb3027e42f1148cf4df1fb4240774d3492b5d12ac0c", + "https://deno.land/std@0.214.0/path/_common/dirname.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8", + "https://deno.land/std@0.214.0/path/_common/format.ts": "92500e91ea5de21c97f5fe91e178bae62af524b72d5fcd246d6d60ae4bcada8b", + "https://deno.land/std@0.214.0/path/_common/from_file_url.ts": "d672bdeebc11bf80e99bf266f886c70963107bdd31134c4e249eef51133ceccf", + "https://deno.land/std@0.214.0/path/_common/glob_to_reg_exp.ts": "2007aa87bed6eb2c8ae8381adcc3125027543d9ec347713c1ad2c68427330770", + "https://deno.land/std@0.214.0/path/_common/normalize.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8", + "https://deno.land/std@0.214.0/path/_common/normalize_string.ts": "dfdf657a1b1a7db7999f7c575ee7e6b0551d9c20f19486c6c3f5ff428384c965", + "https://deno.land/std@0.214.0/path/_common/relative.ts": "faa2753d9b32320ed4ada0733261e3357c186e5705678d9dd08b97527deae607", + "https://deno.land/std@0.214.0/path/_common/strip_trailing_separators.ts": "7024a93447efcdcfeaa9339a98fa63ef9d53de363f1fbe9858970f1bba02655a", + "https://deno.land/std@0.214.0/path/_common/to_file_url.ts": "7f76adbc83ece1bba173e6e98a27c647712cab773d3f8cbe0398b74afc817883", + "https://deno.land/std@0.214.0/path/_interface.ts": "a1419fcf45c0ceb8acdccc94394e3e94f99e18cfd32d509aab514c8841799600", + "https://deno.land/std@0.214.0/path/_os.ts": "8fb9b90fb6b753bd8c77cfd8a33c2ff6c5f5bc185f50de8ca4ac6a05710b2c15", + "https://deno.land/std@0.214.0/path/basename.ts": "5d341aadb7ada266e2280561692c165771d071c98746fcb66da928870cd47668", + "https://deno.land/std@0.214.0/path/common.ts": "03e52e22882402c986fe97ca3b5bb4263c2aa811c515ce84584b23bac4cc2643", + "https://deno.land/std@0.214.0/path/constants.ts": "0c206169ca104938ede9da48ac952de288f23343304a1c3cb6ec7625e7325f36", + "https://deno.land/std@0.214.0/path/dirname.ts": "85bd955bf31d62c9aafdd7ff561c4b5fb587d11a9a5a45e2b01aedffa4238a7c", + "https://deno.land/std@0.214.0/path/extname.ts": "593303db8ae8c865cbd9ceec6e55d4b9ac5410c1e276bfd3131916591b954441", + "https://deno.land/std@0.214.0/path/format.ts": "98fad25f1af7b96a48efb5b67378fcc8ed77be895df8b9c733b86411632162af", + "https://deno.land/std@0.214.0/path/from_file_url.ts": "911833ae4fd10a1c84f6271f36151ab785955849117dc48c6e43b929504ee069", + "https://deno.land/std@0.214.0/path/glob_to_regexp.ts": "83c5fd36a8c86f5e72df9d0f45317f9546afa2ce39acaafe079d43a865aced08", + "https://deno.land/std@0.214.0/path/is_absolute.ts": "4791afc8bfd0c87f0526eaa616b0d16e7b3ab6a65b62942e50eac68de4ef67d7", + "https://deno.land/std@0.214.0/path/is_glob.ts": "a65f6195d3058c3050ab905705891b412ff942a292bcbaa1a807a74439a14141", + "https://deno.land/std@0.214.0/path/join.ts": "ae2ec5ca44c7e84a235fd532e4a0116bfb1f2368b394db1c4fb75e3c0f26a33a", + "https://deno.land/std@0.214.0/path/join_globs.ts": "e9589869a33dc3982101898ee50903db918ca00ad2614dbe3934d597d7b1fbea", + "https://deno.land/std@0.214.0/path/mod.ts": "ffeaccb713dbe6c72e015b7c767f753f8ec5fbc3b621ff5eeee486ffc2c0ddda", + "https://deno.land/std@0.214.0/path/normalize.ts": "4155743ccceeed319b350c1e62e931600272fad8ad00c417b91df093867a8352", + "https://deno.land/std@0.214.0/path/normalize_glob.ts": "98ee8268fad271193603271c203ae973280b5abfbdd2cbca1053fd2af71869ca", + "https://deno.land/std@0.214.0/path/parse.ts": "65e8e285f1a63b714e19ef24b68f56e76934c3df0b6e65fd440d3991f4f8aefb", + "https://deno.land/std@0.214.0/path/posix/_util.ts": "1e3937da30f080bfc99fe45d7ed23c47dd8585c5e473b2d771380d3a6937cf9d", + "https://deno.land/std@0.214.0/path/posix/basename.ts": "39ee27a29f1f35935d3603ccf01d53f3d6e0c5d4d0f84421e65bd1afeff42843", + "https://deno.land/std@0.214.0/path/posix/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4", + "https://deno.land/std@0.214.0/path/posix/constants.ts": "93481efb98cdffa4c719c22a0182b994e5a6aed3047e1962f6c2c75b7592bef1", + "https://deno.land/std@0.214.0/path/posix/dirname.ts": "6535d2bdd566118963537b9dda8867ba9e2a361015540dc91f5afbb65c0cce8b", + "https://deno.land/std@0.214.0/path/posix/extname.ts": "8d36ae0082063c5e1191639699e6f77d3acf501600a3d87b74943f0ae5327427", + "https://deno.land/std@0.214.0/path/posix/format.ts": "185e9ee2091a42dd39e2a3b8e4925370ee8407572cee1ae52838aed96310c5c1", + "https://deno.land/std@0.214.0/path/posix/from_file_url.ts": "951aee3a2c46fd0ed488899d024c6352b59154c70552e90885ed0c2ab699bc40", + "https://deno.land/std@0.214.0/path/posix/glob_to_regexp.ts": "54d3ff40f309e3732ab6e5b19d7111d2d415248bcd35b67a99defcbc1972e697", + "https://deno.land/std@0.214.0/path/posix/is_absolute.ts": "cebe561ad0ae294f0ce0365a1879dcfca8abd872821519b4fcc8d8967f888ede", + "https://deno.land/std@0.214.0/path/posix/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9", + "https://deno.land/std@0.214.0/path/posix/join.ts": "aef88d5fa3650f7516730865dbb951594d1a955b785e2450dbee93b8e32694f3", + "https://deno.land/std@0.214.0/path/posix/join_globs.ts": "ee2f4676c5b8a0dfa519da58b8ade4d1c4aa8dd3fe35619edec883ae9df1f8c9", + "https://deno.land/std@0.214.0/path/posix/mod.ts": "563a18c2b3ddc62f3e4a324ff0f583e819b8602a72ad880cb98c9e2e34f8db5b", + "https://deno.land/std@0.214.0/path/posix/normalize.ts": "baeb49816a8299f90a0237d214cef46f00ba3e95c0d2ceb74205a6a584b58a91", + "https://deno.land/std@0.214.0/path/posix/normalize_glob.ts": "65f0138fa518ef9ece354f32889783fc38cdf985fb02dcf1c3b14fa47d665640", + "https://deno.land/std@0.214.0/path/posix/parse.ts": "d5bac4eb21262ab168eead7e2196cb862940c84cee572eafedd12a0d34adc8fb", + "https://deno.land/std@0.214.0/path/posix/relative.ts": "3907d6eda41f0ff723d336125a1ad4349112cd4d48f693859980314d5b9da31c", + "https://deno.land/std@0.214.0/path/posix/resolve.ts": "bac20d9921beebbbb2b73706683b518b1d0c1b1da514140cee409e90d6b2913a", + "https://deno.land/std@0.214.0/path/posix/separator.ts": "c9ecae5c843170118156ac5d12dc53e9caf6a1a4c96fc8b1a0ab02dff5c847b0", + "https://deno.land/std@0.214.0/path/posix/to_file_url.ts": "7aa752ba66a35049e0e4a4be5a0a31ac6b645257d2e031142abb1854de250aaf", + "https://deno.land/std@0.214.0/path/posix/to_namespaced_path.ts": "28b216b3c76f892a4dca9734ff1cc0045d135532bfd9c435ae4858bfa5a2ebf0", + "https://deno.land/std@0.214.0/path/relative.ts": "ab739d727180ed8727e34ed71d976912461d98e2b76de3d3de834c1066667add", + "https://deno.land/std@0.214.0/path/resolve.ts": "a6f977bdb4272e79d8d0ed4333e3d71367cc3926acf15ac271f1d059c8494d8d", + "https://deno.land/std@0.214.0/path/separator.ts": "c6c890507f944a1f5cb7d53b8d638d6ce3cf0f34609c8d84a10c1eaa400b77a9", + "https://deno.land/std@0.214.0/path/to_file_url.ts": "88f049b769bce411e2d2db5bd9e6fd9a185a5fbd6b9f5ad8f52bef517c4ece1b", + "https://deno.land/std@0.214.0/path/to_namespaced_path.ts": "b706a4103b104cfadc09600a5f838c2ba94dbcdb642344557122dda444526e40", + "https://deno.land/std@0.214.0/path/windows/_util.ts": "d5f47363e5293fced22c984550d5e70e98e266cc3f31769e1710511803d04808", + "https://deno.land/std@0.214.0/path/windows/basename.ts": "e2dbf31d1d6385bfab1ce38c333aa290b6d7ae9e0ecb8234a654e583cf22f8fe", + "https://deno.land/std@0.214.0/path/windows/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4", + "https://deno.land/std@0.214.0/path/windows/constants.ts": "5afaac0a1f67b68b0a380a4ef391bf59feb55856aa8c60dfc01bd3b6abb813f5", + "https://deno.land/std@0.214.0/path/windows/dirname.ts": "33e421be5a5558a1346a48e74c330b8e560be7424ed7684ea03c12c21b627bc9", + "https://deno.land/std@0.214.0/path/windows/extname.ts": "165a61b00d781257fda1e9606a48c78b06815385e7d703232548dbfc95346bef", + "https://deno.land/std@0.214.0/path/windows/format.ts": "bbb5ecf379305b472b1082cd2fdc010e44a0020030414974d6029be9ad52aeb6", + "https://deno.land/std@0.214.0/path/windows/from_file_url.ts": "ced2d587b6dff18f963f269d745c4a599cf82b0c4007356bd957cb4cb52efc01", + "https://deno.land/std@0.214.0/path/windows/glob_to_regexp.ts": "6dcd1242bd8907aa9660cbdd7c93446e6927b201112b0cba37ca5d80f81be51b", + "https://deno.land/std@0.214.0/path/windows/is_absolute.ts": "4a8f6853f8598cf91a835f41abed42112cebab09478b072e4beb00ec81f8ca8a", + "https://deno.land/std@0.214.0/path/windows/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9", + "https://deno.land/std@0.214.0/path/windows/join.ts": "e0b3356615c1a75c56ebb6a7311157911659e11fd533d80d724800126b761ac3", + "https://deno.land/std@0.214.0/path/windows/join_globs.ts": "ee2f4676c5b8a0dfa519da58b8ade4d1c4aa8dd3fe35619edec883ae9df1f8c9", + "https://deno.land/std@0.214.0/path/windows/mod.ts": "7d6062927bda47c47847ffb55d8f1a37b0383840aee5c7dfc93984005819689c", + "https://deno.land/std@0.214.0/path/windows/normalize.ts": "78126170ab917f0ca355a9af9e65ad6bfa5be14d574c5fb09bb1920f52577780", + "https://deno.land/std@0.214.0/path/windows/normalize_glob.ts": "179c86ba89f4d3fe283d2addbe0607341f79ee9b1ae663abcfb3439db2e97810", + "https://deno.land/std@0.214.0/path/windows/parse.ts": "b9239edd892a06a06625c1b58425e199f018ce5649ace024d144495c984da734", + "https://deno.land/std@0.214.0/path/windows/relative.ts": "3e1abc7977ee6cc0db2730d1f9cb38be87b0ce4806759d271a70e4997fc638d7", + "https://deno.land/std@0.214.0/path/windows/resolve.ts": "75b2e3e1238d840782cee3d8864d82bfaa593c7af8b22f19c6422cf82f330ab3", + "https://deno.land/std@0.214.0/path/windows/separator.ts": "e51c5522140eff4f8402617c5c68a201fdfa3a1a8b28dc23587cff931b665e43", + "https://deno.land/std@0.214.0/path/windows/to_file_url.ts": "1cd63fd35ec8d1370feaa4752eccc4cc05ea5362a878be8dc7db733650995484", + "https://deno.land/std@0.214.0/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c", + "https://deno.land/x/postgres@v0.19.3/client.ts": "d141c65c20484c545a1119c9af7a52dcc24f75c1a5633de2b9617b0f4b2ed5c1", + "https://deno.land/x/postgres@v0.19.3/client/error.ts": "05b0e35d65caf0ba21f7f6fab28c0811da83cd8b4897995a2f411c2c83391036", + "https://deno.land/x/postgres@v0.19.3/connection/auth.ts": "db15c1659742ef4d2791b32834950278dc7a40cb931f8e434e6569298e58df51", + "https://deno.land/x/postgres@v0.19.3/connection/connection.ts": "198a0ecf92a0d2aa72db3bb88b8f412d3b1f6b87d464d5f7bff9aa3b6aff8370", + "https://deno.land/x/postgres@v0.19.3/connection/connection_params.ts": "463d7a9ed559f537a55d6928cab62e1c31b808d08cd0411b6ae461d0c0183c93", + "https://deno.land/x/postgres@v0.19.3/connection/message.ts": "20da5d80fc4d7ddb7b850083e0b3fa8734eb26642221dad89c62e27d78e57a4d", + "https://deno.land/x/postgres@v0.19.3/connection/message_code.ts": "12bcb110df6945152f9f6c63128786558d7ad1e61006920daaa16ef85b3bab7d", + "https://deno.land/x/postgres@v0.19.3/connection/packet.ts": "050aeff1fc13c9349e89451a155ffcd0b1343dc313a51f84439e3e45f64b56c8", + "https://deno.land/x/postgres@v0.19.3/connection/scram.ts": "532d4d58b565a2ab48fb5e1e14dc9bfb3bb283d535011e371e698eb4a89dd994", + "https://deno.land/x/postgres@v0.19.3/debug.ts": "8add17699191f11e6830b8c95d9de25857d221bb2cf6c4ae22254d395895c1f9", + "https://deno.land/x/postgres@v0.19.3/deps.ts": "c312038fe64b8368f8a294119f11d8f235fe67de84d7c3b0ef67b3a56628171a", + "https://deno.land/x/postgres@v0.19.3/mod.ts": "4930c7b44f8d16ea71026f7e3ef22a2322d84655edceacd55f7461a9218d8560", + "https://deno.land/x/postgres@v0.19.3/pool.ts": "2289f029e7a3bd3d460d4faa71399a920b7406c92a97c0715d6e31dbf1380ec3", + "https://deno.land/x/postgres@v0.19.3/query/array_parser.ts": "ff72d3e026e3022a1a223a6530be5663f8ebbd911ed978291314e7fe6c2f2464", + "https://deno.land/x/postgres@v0.19.3/query/decode.ts": "3e89ad2a662eab66a4f4e195ff0924d71d199af3c2f5637d1ae650301a03fa9b", + "https://deno.land/x/postgres@v0.19.3/query/decoders.ts": "6a73da1024086ab91e233648c850dccbde59248b90d87054bbbd7f0bf4a50681", + "https://deno.land/x/postgres@v0.19.3/query/encode.ts": "5b1c305bc7352a6f9fe37f235dddfc23e26419c77a133b4eaea42cf136481aa6", + "https://deno.land/x/postgres@v0.19.3/query/oid.ts": "21fc714ac212350ba7df496f88ea9e01a4ee0458911d0f2b6a81498e12e7af4c", + "https://deno.land/x/postgres@v0.19.3/query/query.ts": "510f9a27da87ed7b31b5cbcd14bf3028b441ac2ddc368483679d0b86a9d9f213", + "https://deno.land/x/postgres@v0.19.3/query/transaction.ts": "8f4eef68f8e9b4be216199404315e6e08fe1fe98afb2e640bffd077662f79678", + "https://deno.land/x/postgres@v0.19.3/query/types.ts": "540f6f973d493d63f2c0059a09f3368071f57931bba68bea408a635a3e0565d6", + "https://deno.land/x/postgres@v0.19.3/utils/deferred.ts": "5420531adb6c3ea29ca8aac57b9b59bd3e4b9a938a4996bbd0947a858f611080", + "https://deno.land/x/postgres@v0.19.3/utils/utils.ts": "ca47193ea03ff5b585e487a06f106d367e509263a960b787197ce0c03113a738" + }, "workspace": { "dependencies": [ "jsr:@hono/hono@^4.6.5", "jsr:@std/dotenv@~0.225.2", - "jsr:@wok/pg-driver@~0.19.4", "npm:@clerk/backend@^1.13.9", "npm:@hono/clerk-auth@2", "npm:zod@^3.23.8" diff --git a/schema.sql b/schema.sql index 72b1ec6..574b9e7 100644 --- a/schema.sql +++ b/schema.sql @@ -97,13 +97,13 @@ CREATE TABLE Listing ( description TEXT NOT NULL, is_scraped BOOLEAN NOT NULL DEFAULT FALSE, address VARCHAR(255), + price_formatted VARCHAR(255), price DOUBLE PRECISION NOT NULL CHECK (price >= 0), offer_type_id INT NOT NULL REFERENCES Listing_Type(listing_type_id), property_id INT NOT NULL REFERENCES Property(id) ON DELETE CASCADE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); - CREATE TABLE Price_Change_Log ( id SERIAL PRIMARY KEY, listing_id INTEGER NOT NULL, diff --git a/server.ts b/server.ts index 247f49a..9bbcbf5 100644 --- a/server.ts +++ b/server.ts @@ -2,20 +2,20 @@ import "jsr:@std/dotenv/load"; import { type Context, Hono } from "jsr:@hono/hono"; import { runQuery } from "./config/postgres.ts"; -import { getKvInstance, sendMessage, listenQueue } from "./config/deno-kv.ts"; +import { getKvInstance, listenQueue, sendMessage } from "./config/deno-kv.ts"; const app = new Hono(); const kv = await getKvInstance(); app.get("/", async (c: Context) => { - const postgres = await runQuery("SELECT * FROM property"); + const postgres = await runQuery("SELECT * FROM property LIMIT 10"); await sendMessage({ kv, data: postgres, options: { delay: 5000 } }); - return c.text("Hono!"); + return c.json(postgres.rows); }); app.post("/", async (c: Context) => { const data = await c.req.json(); - await sendMessage({ kv, data, options: { delay: 5000 } }); + await sendMessage({ kv, data, options: { delay: 3000 } }); return c.text("Hono!"); });